Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Sugestão
Este artigo faz parte da secção de Fundamentos para programadores que 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 . Para a referência completa dos operadores, consulte Operadores de acesso a membro e Operadores de coalescência nula na documentação da linguagem.
O C# fornece vários operadores que tornam o código null-safe conciso. Em vez de incorporar if (x != null) guardas por todo o seu código, estes operadores permitem-lhe expressar acesso com segurança nula, valores de substituição e testes nulos numa única expressão.
Este artigo cobre ?. e ?[] para acesso nulo-condicional, ?? para nul-coalescência, ??= para atribuição nula-coalescente e is null/is not null para correspondência de padrões nulos.
Acesso a membros nulos e condicional ?.
O ?. operador acede a um membro apenas quando o objeto não é nulo. Quando o objeto é null, toda a expressão é avaliada como null em vez de lançar um NullReferenceException:
string? name = null;
// Without ?., accessing a member on null throws NullReferenceException:
// int len = name.Length; // throws if name is null
// ?. returns null instead of throwing:
int? len = name?.Length;
Console.WriteLine(len.HasValue); // False
name = "C#";
Console.WriteLine(name?.Length); // 2
O ?. operador faz curto-circuito: quando o lado esquerdo é null, tudo o que está à direita é ignorado. Nenhuma chamada de método é executada e não ocorrem efeitos secundários.
Podes encadear múltiplos ?. operadores numa única expressão. A corrente pára no primeiro null que encontra:
string? input = null;
// Chain ?. across multiple method calls — short-circuits at the first null:
string? upper = input?.Trim()?.ToUpperInvariant();
Console.WriteLine(upper ?? "(none)"); // (none)
input = " hello ";
Console.WriteLine(input?.Trim()?.ToUpperInvariant()); // HELLO
Acesso ao indexador nulo-condicional ?[]
O ?[] operador aplica o mesmo comportamento de curto-circuito ao indexador e ao acesso ao array. Utilize-o quando a própria coleção puder ser null:
string[]? tags = null;
// ?[] accesses an element only when the collection is non-null
string? first = tags?[0];
Console.WriteLine(first ?? "(none)"); // (none)
tags = ["csharp", "dotnet", "nullable"];
Console.WriteLine(tags?[0]); // csharp
Operadores nulos-condicionais em cadeia
Encadeie múltiplos ?. operadores para percorrer um caminho de referências potencialmente nulas. A cadeia entra em curto-circuito no primeiro null:
var order = new Order("ORD-001", null);
// Each ?. short-circuits when null: Customer is null, so Address and City are never accessed
string? city = order.Customer?.Address?.City;
Console.WriteLine(city ?? "(no city)"); // (no city)
var fullOrder = new Order("ORD-002",
new Customer("Alice", new Address("123 Main St", "Springfield", "IL")));
Console.WriteLine(fullOrder.Customer?.Address?.City); // Springfield
Quando Customer é null, nem Address nem City é avaliado. A expressão total retorna null.
Invocação de delegado seguro para threads
?. fornece uma forma limpa e segura de threads para invocar um delegado ou levantar um evento. A expressão do delegado é avaliada apenas uma vez, pelo que não há janela para outro thread cancelar a subscrição entre a verificação nula e a invocação:
EventHandler? clicked = null;
// No subscribers — ?.Invoke does nothing instead of throwing NullReferenceException
clicked?.Invoke(null, EventArgs.Empty);
clicked += (_, _) => Console.WriteLine("Button clicked!");
// With a subscriber — ?.Invoke calls the handler
clicked?.Invoke(null, EventArgs.Empty);
// Output: Button clicked!
Este padrão substitui o antigo if (clicked != null) clicked(...) idioma.
Coalescência nula ??
O ?? operador devolve o seu operando da mão esquerda quando este não é nulo, e o seu operando da mão direita quando o da esquerda é null. Use-o para fornecer um valor padrão:
string? username = null;
// ?? returns the right-hand value when the left-hand is null
string display = username ?? "Guest";
Console.WriteLine(display); // Guest
username = "alice";
display = username ?? "Guest";
Console.WriteLine(display); // alice
?? é associativo à direita, por isso a ?? b ?? c é avaliado como a ?? (b ?? c). O primeiro valor não nulo vence. Um padrão comum é encadear ?. com ??: use ?. para atravessar em segurança uma cadeia nula-possível, depois use ?? para substituir por um valor padrão se a cadeia devolveu null. Para um exemplo completo, veja Combinar operadores nulos.
Atribuição de coalescência nula ??=
O ??= operador atribui o valor direito a uma variável apenas quando a variável é null. Use-o para inicialização preguiçosa (lazy initialization):
List<string>? cache = null;
// ??= assigns only when the variable is null
cache ??= LoadData();
Console.WriteLine(cache.Count); // 3
// cache is already non-null, so LoadData() isn't called again
cache ??= LoadData();
Console.WriteLine(cache.Count); // 3
static List<string> LoadData() => ["alpha", "beta", "gamma"];
A expressão à direita é avaliada apenas quando a variável possui o valor null. Quando a variável já tem um valor, o lado direito não é avaliado de todo.
Atribuição nulo-condicional (C# 14)
A partir de C# 14, podes usar ?. e ?[] como alvos de atribuição. A atribuição só é executada quando o objeto da esquerda não é nulo:
AppConfig? config = new AppConfig();
// Assigns only when config is non-null (C# 14)
config?.Theme = "dark";
Console.WriteLine(config?.Theme); // dark
AppConfig? missing = null;
missing?.Theme = "light"; // no-op: missing is null
Console.WriteLine(missing?.Theme ?? "(no config)"); // (no config)
O lado direito é avaliado apenas quando se sabe que o lado esquerdo não é nulo.
Correspondência de padrões nulos: is null e is not null
Os is null padrões e is not null testam se uma expressão é null:
string? input = null;
// is null is the preferred test — unaffected by operator overloading
if (input is null)
{
Console.WriteLine("No input provided.");
}
// == null also works, but a custom == operator can change its behavior
if (input == null)
{
Console.WriteLine("Still no input.");
}
Prefiro is null em vez == null para verificações de nulo. O == operador pode ser sobrecarregado, o que significa que x == null pode retornar true mesmo que x não seja null, se o tipo definir um operador de igualdade personalizado. O is null padrão testa sempre a referência nula real, independentemente da sobrecarga do operador.
string? value = "hello";
if (value is not null)
{
Console.WriteLine(value.ToUpper()); // HELLO
}
Combinar operadores nulos
Na prática, muitas vezes combina-se vários destes operadores. Uma expressão pode percorrer com segurança um grafo de objetos profundos, aplicar um fallback e depois proteger o resultado:
Order? order = GetPendingOrder();
// Chain ?. for safe traversal, ?? for a fallback, is null for a clear guard
string city = order?.Customer?.Address?.City ?? "unknown";
if (order is null)
{
Console.WriteLine("No pending order.");
}
else
{
Console.WriteLine($"Shipping to: {city}");
}
// Output: No pending order.
Operador de perdão nulo !
O ! operador postfix suprime avisos anuláveis. Anexe ! para dizer ao compilador "esta expressão definitivamente não é nula." O operador não tem efeito em tempo de execução. Só afeta a análise do estado nulo do compilador.
string? name = FindUser("alice");
// Use ! only when you have information the compiler doesn't.
// FindUser guarantees a non-null result for known usernames.
int length = name!.Length;
Console.WriteLine(length); // 5
Usa ! com moderação, e só quando tiveres informação que o compilador não tem. Exemplos incluem testes que passam null intencionalmente para validar a lógica de verificação de argumentos, ou chamar um método cujo contrato garante um retorno não nulo para uma entrada conhecida. O uso ! excessivo anula o propósito dos tipos de referência anuláveis. Para uma explicação completa, veja Tipos de referência anuláveis.