Segurança nula em C#

Sugestão

Este artigo faz parte da secção Fundamentos para programadores que já conhecem pelo menos uma linguagem de programação e estão a aprender C#. Se és novo na programação, começa primeiro pelos tutoriais para começar .

Vens de Java ou C++? O C# fornece segurança nula em tempo de compilação através de tipos de referência anuláveis. O objetivo é semelhante às anotações @NonNull da Java, mas é imposto pelo compilador. C# também tem operadores dedicados como ?. e ?? que tornam expressões nulas seguras concisas.

null representa a ausência de um valor. Quando tenta aceder a um membro numa null referência, chamando um método ou lendo uma propriedade, o runtime gera um NullReferenceException:

// Accessing a member on null throws NullReferenceException at runtime:
// string? name = null;
// int length = name.Length; // throws NullReferenceException

// Check before you dereference:
string? name = null;
if (name is not null)
{
    Console.WriteLine($"Name has {name.Length} characters.");
}
else
{
    Console.WriteLine("Name has no value.");
}
// Output: Name has no value.

O C# dá-lhe três ferramentas complementares para escrever código null-safe:

  • Tipos de valor anuláveis: permitem que um tipo de valor como int ou bool também possa conter null
  • Tipos de referência anuláveis: deixe o compilador acompanhar se uma referência pode ser null
  • Operadores nulos: expressam de forma concisa o acesso seguro a nulos e a lógica de recadação

Tipos de valor anulável

Tipos de valor como int, double e bool não conseguem conter null por defeito. Adicione ? ao nome do tipo para criar um tipo de valor nulo que contém um valor ou null:

int? score = null;
Console.WriteLine(score.HasValue);               // False

score = 95;
Console.WriteLine(score.HasValue);               // True
Console.WriteLine(score.GetValueOrDefault());    // 95

int? missing = null;
Console.WriteLine(missing.GetValueOrDefault(-1)); // -1

Os tipos de valor anulável são úteis quando um tipo de valor subjacente precisa de representar "nenhum dado". Cenários comuns incluem colunas da base de dados que podem estar ausentes, definições de configuração opcionais e leituras de sensores que ainda não foram capturadas.

Para obter uma cobertura completa de declaração, verificação e conversão, veja Tipos de valor nulo.

Tipos de referência anuláveis

Tipos de referência, como string, arrays e instâncias de classe, podem armazenar null em tempo de execução. Tipos de referência anuláveis são uma funcionalidade do compilador que torna explícita a intenção nula e detetam erros em tempo de compilação.

Ao usar a ? anotação, declara a sua intenção:

  • string? — esta referência pode ser null; o compilador avisa se a desreferenciar sem verificar primeiro
  • string— esta referência não deve ser null; o compilador avisa se lhe atribuir null
// string?  means this reference might be null
// string   means this reference should not be null
string? nullableName = null;
string  nonNullName  = "Alice";

// ?. safely accesses a member when the reference might be null
string display = nullableName?.ToUpper() ?? "(no name)";
Console.WriteLine(display);         // (no name)

display = nonNullName.ToUpper();    // safe: nonNullName is never null
Console.WriteLine(display);         // ALICE

Todos os projetos .NET criados por modelos modernos de SDK permitem por defeito tipos de referência anuláveis. Para orientações completas sobre como ativar e anotar, veja Tipos de referência anuláveis.

Operadores nulos

C# inclui vários operadores que permitem escrever código null-safe sem guardas manuais if-null em todo o lado:

Operator Name Purpose
?. Acesso a membros nulos e condicional Acede a um membro apenas quando o objeto não é nulo
?[] Acesso ao indexador com condição nula Acede a um elemento apenas quando a coleção não for nula
?? Coalescência nula Devolver um valor de reserva quando a expressão é null
??= Atribuição de coalescência nula Atribuir apenas quando a variável é null
is null / is not null Padrão nulo Teste nulo preferencial
string? city = GetCity();

// ?. — access a member only when non-null
int? len = city?.Length;

// ?? — substitute a default when null
string display = city ?? "unknown";

// is null — preferred null test
if (city is null)
{
    Console.WriteLine("No city provided.");
}
else
{
    Console.WriteLine($"{display} ({len} chars)");
}
// Output: No city provided.

Para exemplos detalhados de cada operador, veja Operadores nulos.

Os tipos de valor nulo e os tipos de referência anuláveis servem propósitos diferentes

Tipos de valor nulo e tipos de referência anuláveis não são alternativas. Resolvem diferentes problemas:

  • Use T? para um tipo de valor que precise de representar "nenhum valor". Por exemplo, use int? para uma coluna opcional de base de dados ou DateTime? para um evento que ainda não está agendado.
  • Use string? e outras anotações de referência anuláveis para documentar que uma referência pode ser null, para que o compilador possa avisá-lo antes de ocorrer NullReferenceException em tempo de execução.

Juntas, estas funcionalidades e os operadores null dão-lhe um conjunto completo de ferramentas para escrever código C# nulo-seguro.