Noções básicas sobre nulidade
Se você for desenvolvedor do .NET, é provável que tenha encontrado o System.NullReferenceException. Isso ocorre em tempo de execução quando um null é desreferenciado, ou seja, quando uma variável é avaliada em runtime, mas a variável se refere a null. Essa exceção é, de longe, a exceção mais comum dentro do ecossistema do .NET. O criador de null, Sir Tony Hoare, refere-se null a como o "erro de um bilhão de dólares".
No exemplo a seguir, a variável FooBar é atribuída a null e imediatamente desreferenciada, exibindo o problema:
// Declare variable and assign it as null.
FooBar fooBar = null;
// Dereference variable by calling ToString.
// This will throw a NullReferenceException.
_ = fooBar.ToString();
// The FooBar type definition.
record FooBar(int Id, string Name);
O problema se torna muito mais difícil de identificar como desenvolvedor quando seus aplicativos crescem em tamanho e complexidade. Identificar possíveis erros como esse é um trabalho para ferramentas e o compilador do C# está aqui para ajudar.
Definindo a segurança nula
O termo segurança nula define um conjunto de recursos específicos para tipos anuláveis que ajudam a reduzir o número de ocorrências possíveis NullReferenceException .
Considerando o exemplo FooBar anterior, você pode evitar o NullReferenceException verificando se a variável fooBar era null antes de desreferenciá-la:
// Declare variable and assign it as null.
FooBar fooBar = null;
// Check for null
if (fooBar is not null)
{
_ = fooBar.ToString();
}
// The FooBar type definition for example.
record FooBar(int Id, string Name);
Para ajudar a identificar cenários como esse, o compilador pode inferir a intenção do código e impor o comportamento desejado. No entanto, isso é somente quando um contexto anulável é habilitado. Antes de discutir o contexto anulável, vamos descrever os possíveis tipos anuláveis.
Tipos anuláveis
Antes do C# 2.0, somente os tipos de referência eram anuláveis. Tipos de valor como int ou DateTimenão podem ser null. Se esses tipos são inicializados sem um valor, eles fazem fallback para seu valor default. No caso de um int, é 0. Para um DateTime, é DateTime.MinValue.
Os tipos de referência instanciados sem valores iniciais funcionam de maneira diferente. O valor default de todos os tipos de referência é null.
Considere o trecho de C# a seguir:
string first; // first is null
string second = string.Empty // second is not null, instead it's an empty string ""
int third; // third is 0 because int is a value type
DateTime date; // date is DateTime.MinValue
No exemplo anterior:
firsténullporque o tipo de referênciastringfoi declarado, mas nenhuma atribuição foi feita.secondé atribuídostring.Emptyquando é declarado. O objeto nunca teve uma atribuiçãonull.thirdé0, apesar de não ter sido atribuído. É um (tipo-valor)structe tem um valordefaultde0.datetem a inicialização cancelada, mas o valordefaultdele é System.DateTime.MinValue.
A partir do C# 2.0, você pode definir tipos de valor anuláveis usando Nullable<T> (ou T? para abreviação). Isso permite que os tipos de valor sejam anuláveis. Considere o trecho de C# a seguir:
int? first; // first is implicitly null (uninitialized)
int? second = null; // second is explicitly null
int? third = default; // third is null as the default value for Nullable<Int32> is null
int? fourth = new(); // fourth is 0, since new calls the nullable constructor
No exemplo anterior:
firsténullporque o tipo que permite valor nulo é não reinicializado.secondé atribuídonullquando é declarado.thirdénullassim como o valordefaultparaNullable<int>énull.fourthé0assim como a expressãonew()chama o construtorNullable<int>einté0por padrão.
O C# 8.0 introduziu tipos de referência anuláveis, em que você pode expressar sua intenção de que um tipo de referência possa ser null ou sempre não sejanull. Você pode estar pensando: "Achei que os tipos de referência eram anuláveis". Você não está errado, e eles são. Esse recurso permite que você expresse sua intenção, que o compilador tenta impor. A mesma sintaxe T? expressa que um tipo de referência deve ser anulável.
Considere o trecho de C# a seguir:
#nullable enable
string first = string.Empty;
string second;
string? third;
Dado o exemplo anterior, o compilador infere sua intenção da seguinte maneira:
firstnuncanullé como definitivamente atribuído.secondnunca deve sernull, mesmo que seja inicialmentenull. Avaliandosecondantes de atribuir um valor resulta em um aviso do compilador, pois ele é não inicializado.thirdpode sernull. Por exemplo, ele pode apontar para umSystem.String, mas pode apontar paranull. Qualquer uma dessas variações é aceitável. O compilador ajuda você com um aviso se você desreferenciarthirdsem primeiro verificar se ele não é nulo.
Importante
Para usar o recurso de tipos de referência que permitem valor nulo, conforme mostrado acima, ele deve estar dentro de um contexto anulável. Isso é detalhado na próxima seção.
Contexto que permite valor nulo
Contextos anuláveis permitem um controle refinado de como o compilador interpreta variáveis de tipo de referência. Há quatro contextos anuláveis possíveis:
disable: o compilador se comporta da mesma forma que o C# 7.3 e anterior.enable: o compilador habilita toda a análise de referência nula e todos os recursos de linguagem.warnings: o compilador executa todas as análises nulas e emite avisos quando o código pode ser desreferenciadonull.annotations: o compilador não executa análise nula ou emite avisos quando o código pode ser desreferenciadonull, mas você ainda pode anotar seu código usando tipos de referência anuláveis?e operadores tolerantes a nulos (!).
Este módulo tem como escopo os contextos anuláveis disable ou enable. Para obter mais informações, consulte tipos de referência anuláveis: contextos anuláveis.
Habilitar tipos de referência anuláveis
No arquivo de projeto C# (.csproj), adicione um nó filho <Nullable> ao <Project> elemento (ou acrescente a um existente <PropertyGroup>). Isso aplicará o contexto anulável enable a todo o projeto.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<!-- Omitted for brevity -->
</Project>
Como alternativa, você pode definir o escopo do contexto anulável para um arquivo C# usando uma diretiva do compilador.
#nullable enable
A diretiva de compilador C# anterior é funcionalmente equivalente à configuração do projeto, mas tem como escopo o arquivo no qual ele reside. Para obter mais informações, consulte tipos de referência anuláveis: contextos anuláveis (documentação)
Importante
O contexto anulável é habilitado no arquivo .csproj por padrão em todos os modelos de projeto C# começando com o .NET 6.0 e superior.
Quando o contexto anulável estiver habilitado, você receberá novos avisos. Considere o exemplo FooBar anterior, que tem dois avisos quando analisado em um contexto anulável:
A linha
FooBar fooBar = null;tem um aviso sobre a atribuiçãonull: Aviso CS8600 do C#: Convertendo o literal nulo ou o possível valor nulo em um tipo não anulável.A linha
_ = fooBar.ToString();também tem um aviso. Desta vez, o compilador está preocupado quefooBarpossa ser nulo: Aviso CS8602: Desreferência de uma referência possivelmente nula.
Importante
Não há segurança nula garantida , mesmo que você reaja e elimine todos os avisos. Há alguns cenários limitados que passarão na análise do compilador, ainda que resultem em uma NullReferenceException de runtime.
Resumo
Nesta unidade, você aprendeu a habilitar um contexto anulável em C# para ajudar a protegê-lo de NullReferenceException. Na próxima unidade, você aprenderá mais sobre como expressar explicitamente sua intenção em um contexto anulável.

