Observe que não há motivo para que haja um aviso na chamada para Console.WriteLine()
. A propriedade do tipo de referência não é um tipo anulável e, portanto, não é necessário que o compilador avise que pode ser nulo.
Você pode argumentar que o compilador deve avisar sobre a referência em struct
si. Isso me pareceria razoável. Mas isso não acontece. Isso parece ser uma brecha, causada pela inicialização padrão para tipos de valor, ou seja, sempre deve haver um construtor padrão (sem parâmetros), que sempre zera todos os campos (nulos para campos de tipo de referência, zeros para tipos numéricos etc.) )
Eu chamo isso de brecha, porque, em teoria, os valores de referência não nulos devem, de fato, ser sempre nulos! Duh. :)
Esta brecha parece ser abordada neste artigo do blog: Apresentando tipos de referência nulos em C #
Evitando nulos Até agora, os avisos eram sobre a proteção de nulos em referências anuláveis de serem desreferenciadas. O outro lado da moeda é evitar ter nulos nas referências não anuláveis.
Existem algumas maneiras pelas quais os valores nulos podem surgir, e a maioria deles vale a pena alertar, enquanto alguns deles causariam outro “mar de avisos” que é melhor evitar:
…
- Usando o construtor padrão de uma estrutura que possui um campo de tipo de referência não anulável. Este é sorrateiro, já que o construtor padrão (que zera a estrutura) pode até ser usado implicitamente em muitos lugares. Provavelmente é melhor não avisar [grifo meu - PD] , ou então muitos tipos de estruturas existentes seriam inúteis.
Em outras palavras, sim, isso é uma brecha, mas não, não é um bug. Os designers de idiomas estão cientes disso, mas optaram por deixar esse cenário de fora dos avisos, porque fazer o contrário seria impraticável, dada a maneira como a struct
inicialização funciona.
Observe que isso também está de acordo com a filosofia mais ampla por trás do recurso. Do mesmo artigo:
Então, queremos que ele se queixe do seu código existente. Mas não desagradável. Eis como vamos tentar encontrar esse equilíbrio:
…
- Não há segurança nula garantida [ênfase minha - PD] , mesmo que você reaja e elimine todos os avisos. Existem muitos buracos na análise por necessidade e outros por opção.
Até o último ponto: Às vezes, um aviso é a coisa "correta" a ser executada, mas dispara o tempo todo no código existente, mesmo quando ele é realmente escrito de maneira nula e segura. Nesses casos, erraremos por conveniência, não por correção. Não podemos estar produzindo um “mar de avisos” no código existente: muitas pessoas simplesmente desativariam os avisos e nunca se beneficiariam disso.
Observe também que esse mesmo problema existe com matrizes de tipos de referência nominalmente não nulos (por exemplo string[]
). Quando você cria a matriz, todos os valores de referência são null
e, no entanto, isso é legal e não gera nenhum aviso.
Tanta coisa para explicar por que as coisas são do jeito que são. Então a pergunta se torna: o que fazer sobre isso? Isso é muito mais subjetivo, e não acho que haja uma resposta certa ou errada. Dito isto…
Eu pessoalmente trataria meus struct
tipos caso a caso. Para aqueles em que a intenção é realmente um tipo de referência anulável, eu aplicaria a ?
anotação. Caso contrário, eu não faria.
Tecnicamente, todo valor de referência em um struct
deve ser "anulável", ou seja, incluir a ?
anotação anulável com o nome do tipo. Mas, como ocorre com muitos recursos semelhantes (como assíncrono / aguardar em C # ou const
em C ++), isso tem um aspecto "infeccioso", pois você precisará substituir essa anotação posteriormente (com a !
anotação) ou incluir uma verificação nula explícita , ou apenas atribua esse valor a outra variável de tipo de referência anulável.
Para mim, isso anula muito a finalidade de habilitar tipos de referência anuláveis. Como esses membros de struct
tipos exigirão tratamento de caso especial em algum momento, e como a única maneira de lidar com segurança com segurança e ainda poder usar tipos de referência não nulos é colocar verificações nulas em todos os lugares em que você usar struct
, eu sinto que é uma opção de implementação razoável aceitar que, quando o código inicializa struct
, é responsabilidade do código fazê-lo corretamente e garantir que o membro do tipo de referência não anulável seja realmente inicializado com um valor não nulo.
Isso pode ser auxiliado fornecendo um meio "oficial" de inicialização, como um construtor não padrão (por exemplo, um com parâmetros) ou um método de fábrica. Sempre haverá sempre o risco de usar o construtor padrão, ou nenhum construtor (como nas alocações de matriz), mas fornecendo um meio conveniente para inicializar o struct
correto, isso incentivará o código que o usa para evitar referências nulas em variáveis anuláveis.
Dito isso, se o que você deseja é 100% de segurança em relação aos tipos de referência anuláveis, então claramente a abordagem correta para esse objetivo específico é sempre anotar todos os membros do tipo de referência em um struct
com ?
. Isso significa todos os campos e propriedades implementadas automaticamente, juntamente com qualquer método ou método de obtenção de propriedades que retorne diretamente esses valores ou o produto desses valores. Em seguida, o código de consumo precisará incluir verificações nulas ou o operador que perdoa nulas em todos os pontos em que esses valores são copiados em variáveis não nulas.