.NET 10 e C# 14: Mudanças no Null
O tratamento de null no C# 14 ganhou um novo capítulo com o .NET 10, e entender essa evolução ajuda você a escrever código mais curto e mais seguro. Afinal, valores nulos sempre foram uma fonte de atenção (e de dor de cabeça) para quem desenvolve em C#.
Desde exceções inesperadas até lógica defensiva excessiva, o tratamento de valores nulos é um tema recorrente em qualquer base de código. Por isso, a linguagem vem oferecendo ferramentas cada vez mais sofisticadas para prevenir erros.
Neste artigo, você vai ver o que são nulos, como a NullReferenceException acontece e como os warnings de compilação ajudam. Em seguida, vamos revisar os operadores de coalescência nula e, por fim, chegar à atribuição condicional nula (?.=) do C# 14.
O que são nulos no C#
Em C#, null representa a ausência de valor em tipos por referência, como classes, interfaces, arrays e delegates. Ou seja, a variável existe, mas não aponta para nenhuma instância válida.
string? name = null;
Nulls existem como uma forma de representar a ausência de um valor ou de um objeto. Por exemplo, eles indicam que uma informação ainda não foi fornecida ou que um recurso está indisponível.
Também servem para sinalizar que não há um valor aplicável em determinado contexto. No entanto, essa utilidade tem um preço: acessar membros de uma referência nula gera exceções em tempo de execução.
Como o null funciona na memória
Na memória, tipos por referência armazenam um ponteiro para o endereço onde o objeto está alocado. Quando esse ponteiro vale null, ele não aponta para nenhuma instância.
Isso acontece porque, em muitos casos, precisamos declarar uma variável em um escopo e preenchê-la em outro. Consequentemente, até a atribuição acontecer, o valor dessa variável fica nulo.
Historicamente, o compilador não impedia o acesso a membros de variáveis nulas. Dessa forma, o modelo obrigava o desenvolvedor a adotar práticas defensivas em todo lugar.
Vale lembrar que tipos por valor, como int e DateTime, não aceitam null por padrão. Para eles, a linguagem oferece os nullable value types, escritos como int?, que são outro assunto.
NullReferenceException: o erro mais comum
A famosa NullReferenceException ocorre quando tentamos acessar um membro (propriedade, método, indexador) de uma referência nula. Veja um exemplo simples:
Person person = null;
Console.WriteLine(person.Name);
// lança NullReferenceException
Esse tipo de erro é comum em aplicações que não validam a existência de um objeto antes de acessá-lo, e continua possível com o null no C# 14. Além disso, ele costuma aparecer longe da origem do problema, o que dificulta o diagnóstico.
Para evitar a exceção, o caminho clássico é verificar explicitamente se a referência é nula antes de usá-la:
if (person is not null)
{
Console.WriteLine(person.Name);
}
Embora funcione, essa abordagem se torna repetitiva em sistemas grandes. Afinal, muitos objetos circulam por diferentes camadas da aplicação.
O excesso de verificações polui o código e dificulta a leitura. Por outro lado, basta esquecer uma única checagem para que a exceção apareça em produção.
Foi esse desafio que levou à criação dos operadores ?., ?? e, mais recentemente, da atribuição condicional que define o tratamento de null no C# 14.
Nullable reference types e os warnings de compilação
A partir do C# 8, o compilador passou a oferecer os nullable reference types. Com eles, você indica explicitamente quando um tipo por referência pode ou não ser nulo.
No contexto de null no C# 14, essa base continua a mesma.
Na prática, string passa a significar "nunca nulo" e string? passa a significar "talvez nulo". Contudo, a anotação não muda o tipo em tempo de execução, porque as duas formas continuam sendo System.String.
A diferença está na análise estática. O compilador acompanha o estado de cada variável (not-null ou maybe-null) e emite warnings quando o código pode desreferenciar um valor nulo.
Como habilitar o contexto nullable
Desde o .NET 6, os templates de projeto já incluem a configuração abaixo no .csproj. Portanto, em um projeto novo do .NET 10 você normalmente não precisa fazer nada.
<PropertyGroup>
<Nullable>enable</Nullable>
</PropertyGroup>
Em projetos antigos, o contexto vem desabilitado por padrão. Nesse caso, você pode habilitá-lo arquivo por arquivo com a diretiva #nullable enable e migrar aos poucos.
A própria Microsoft descreve essas abordagens no guia de estratégias de migração para nullable. Além disso, a documentação recomenda não desabilitar a análise em projetos de produção.
Principais warnings de null no C# 14
Os warnings de null no C# 14 são os mesmos introduzidos junto com os nullable reference types. Entre os mais frequentes no dia a dia, estão estes:
- CS8600: conversão de um literal nulo ou possível valor nulo para um tipo não anulável.
- CS8602: desreferência de uma referência possivelmente nula.
- CS8603: possível retorno de referência nula.
- CS8618: membro não anulável que sai do construtor sem valor.
O CS8618 é especialmente comum em classes de modelo. Por exemplo, a classe abaixo dispara o warning nas duas propriedades:
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
Para resolver, você pode receber os valores no construtor, inicializar com string.Empty ou marcar as propriedades como string?. A escolha depende da regra de negócio, e não apenas do warning.
Se você quiser se aprofundar nesse tema, o artigo entendendo nullable types no .NET detalha os tipos anuláveis com mais exemplos.
Operadores de coalescência nula: ?? e ??=
A coalescência nula é um recurso importante para fornecer valores padrão quando algo vale null. O operador ?? devolve o operando da esquerda quando ele não é nulo e, caso contrário, devolve o da direita.
string? name = null;
string finalName = name ?? "Unknown";
Já o operador ??= faz a atribuição direta, mas somente quando a variável ainda está nula:
name ??= "Guest";
Um detalhe importante é que o lado direito do ??= só roda quando a variável vale null. Por isso, ele funciona muito bem para inicialização preguiçosa, como em um cache carregado sob demanda.
List<string>? cache = null;
// LoadData só é chamado na primeira vez
cache ??= LoadData();
cache ??= LoadData();
static List<string> LoadData() => ["alpha", "beta", "gamma"];
Esses operadores reduzem a necessidade de verificações explícitas e seguem válidos com o null no C# 14. Assim, o código fica mais enxuto e mais expressivo.
Operador condicional nulo: ?. e ?[]
Antes de falar da novidade, vale revisar o operador condicional nulo, disponível desde o C# 6. Ele permite acessar um membro somente quando o objeto não é nulo.
int? length = customer?.Name?.Length;
string? firstTag = tags?[0];
Se customer ou Name forem nulos, a expressão inteira resulta em null, sem exceção. Além disso, o operador avalia o operando da esquerda uma única vez.
É comum combinar ?. com ?? para obter um valor final não nulo:
int length = customer?.Name?.Length ?? 0;
Até o C# 13, porém, esse operador só funcionava para leitura. Ou seja, você não podia usá-lo do lado esquerdo de uma atribuição, e é justamente isso que muda com o null no C# 14.
Atribuição condicional nula: a novidade de null no C# 14
A principal novidade de null no C# 14 é o suporte aos operadores ?. e ?[] no lado esquerdo de uma atribuição. O recurso se chama null-conditional assignment e faz parte da lista oficial de novidades do C# 14.
O C# 14 roda no .NET 10, lançado em novembro de 2025. Portanto, basta usar o SDK do .NET 10 para ter acesso à nova sintaxe.
Antes e depois do null no C# 14
Antes, para atribuir uma propriedade de um objeto que podia ser nulo, era preciso escrever um if:
if (customer is not null)
{
customer.Order = GetCurrentOrder();
}
Agora, com o null no C# 14, a mesma intenção cabe em uma linha:
customer?.Order = GetCurrentOrder();
Essa sintaxe reduz o código e melhora a legibilidade. Além disso, o lado direito da expressão (GetCurrentOrder()) só é avaliado quando customer não é nulo.
O mesmo vale para indexadores. Nesse caso, a atribuição ocorre apenas se o array ou a coleção existir:
messages?[5] = "five";
values?[2] = GenerateNextIndex();
Atribuição composta e eventos
Além da atribuição simples, o recurso também funciona com atribuições compostas, como += e -=:
customer?.Total += 100;
customer?.Total -= discount;
Um dos cenários que motivaram a proposta foi justamente a inscrição em eventos em código de interface. Com a nova sintaxe, você assina um evento apenas quando o objeto existe:
button?.Clicked += OnButtonClicked;
Outro motivo citado na especificação é a paridade entre propriedades e métodos Set(). Afinal, customer?.SetOrder(order) sempre foi válido, enquanto customer?.Order = order não era.
Limitações da atribuição condicional
No entanto, os operadores de incremento e decremento (++ e --) não funcionam com a nova sintaxe. Por isso, as linhas abaixo geram erro de compilação:
customer?.Visits++; // erro
--customer?.Visits; // erro
A especificação também lista outras restrições. Por exemplo, a expressão condicional continua sem ser uma variável, então você não pode passá-la como argumento ref ou out.
Também não é possível atribuir por desconstrução, como em (a?.b, c?.d) = (x, y). Nesse caso, a recomendação é escrever duas atribuições separadas.
Por fim, o recurso em geral não funciona quando o receptor é um tipo por valor. Assim, ele foi pensado para objetos de tipos por referência, que é onde o null realmente aparece.
Como o null no C# 14 é avaliado
Entender a semântica evita surpresas. Segundo a especificação da atribuição condicional nula, P?.A = B equivale a if (P is not null) P.A = B;.
A diferença é que P é avaliado uma única vez. Isso importa quando P é uma chamada de método ou uma expressão com efeitos colaterais.
Quando você usa o resultado da expressão, o comportamento muda um pouco. Nesse caso, P?.A = B equivale a (P is null) ? (T?)null : (P.A = B).
Em outras palavras, a expressão devolve null se o receptor for nulo e devolve o valor atribuído caso contrário. Veja um exemplo com encadeamento:
// CalculateDiscount só executa se order e order.Customer não forem nulos
order?.Customer?.Discount = CalculateDiscount();
Aqui, o compilador verifica order e, em seguida, order.Customer. Assim, CalculateDiscount() só roda quando os dois existem.
Outro ponto curioso é a combinação com ??=. A linha a?.b ??= c; atribui c somente se a existir e se a.b ainda estiver nulo.
// Cria o endereço só quando o cliente existe e ainda não tem endereço
customer?.Address ??= new Address();
Esse padrão é útil para inicializar propriedades opcionais sem escrever dois if aninhados. Portanto, ele resume bem a proposta do null no C# 14: menos cerimônia com o mesmo nível de segurança.
O que muda no .NET 10 para o tratamento de null no C# 14
O C# 14 é a versão da linguagem suportada oficialmente pelo .NET 10. Por isso, ao criar um projeto com o SDK do .NET 10, a atribuição condicional nula já está disponível sem configuração extra.
Os nullable reference types continuam funcionando da mesma forma que nas versões anteriores. Na prática, o que o .NET 10 traz para o tema é a nova sintaxe de atribuição, somada a toda a base que já existia.
Vale notar que o C# 14 trouxe outras novidades no mesmo pacote, como a palavra-chave field e os extension members. Se esse último tema interessa, o artigo sobre o novo modelo de extension members no C# 14 aprofunda o assunto.
Já a atribuição condicional tem um texto dedicado aqui no blog. O artigo atribuição condicional nula no C# 14 traz mais exemplos focados só nesse operador.
Para quem está migrando de versões antigas, a recomendação é simples. Primeiro, habilite o contexto nullable; em seguida, resolva os warnings; por fim, use a nova sintaxe para simplificar as verificações que sobraram.
Boas práticas com null no C# 14
A nova sintaxe é poderosa, mas não substitui um bom desenho de tipos. Dessa forma, algumas práticas continuam valendo mesmo com o null no C# 14.
- Deixe o contexto nullable ligado: os warnings apontam problemas antes de eles chegarem à produção.
- Prefira tipos não anuláveis: use
?apenas quando a ausência de valor fizer sentido para o domínio. - Evite o operador
!como atalho: ele apenas silencia o warning, enquanto o risco continua no código. - Use
?.=para efeitos opcionais: ele funciona bem quando ignorar o objeto nulo é o comportamento correto. - Valide entradas nas bordas: em métodos públicos,
ArgumentNullException.ThrowIfNulldeixa a intenção clara.
Um cuidado importante é não esconder erros com a nova sintaxe. Se um objeto nunca deveria ser nulo, a atribuição condicional vai simplesmente ignorar a linha, e o bug passa despercebido.
Nesse caso, é melhor deixar a exceção acontecer ou validar a entrada logo no início do método. Afinal, falhar cedo costuma ser mais barato do que falhar em silêncio.
public void UpdateOrder(Customer customer, Order order)
{
// Falha cedo quando o contrato do método não é respeitado
ArgumentNullException.ThrowIfNull(customer);
customer.Order = order;
}
Por outro lado, quando o objeto é opcional por natureza, o ?.= deixa o código mais limpo. Um exemplo clássico é atualizar um componente de interface que pode ainda não ter sido criado.
Conclusão
Nulls continuarão existindo, mas agora temos mais controle, segurança e produtividade ao lidar com eles. Os nullable reference types avisam sobre riscos, os operadores ?? e ??= fornecem valores padrão e o ?. lê dados com segurança.
Com o null no C# 14, o ciclo se completa com a atribuição condicional. Assim, o if defensivo de três linhas vira uma única instrução, sem perder a garantia de que nada roda sobre um objeto nulo.
A evolução constante da linguagem mostra um forte compromisso com código mais robusto e expressivo. Por isso, se você ainda não usa o contexto nullable e os operadores de coalescência, talvez seja a hora de reconsiderar.
O próximo passo é abrir um projeto no .NET 10, ligar o contexto nullable e experimentar a nova sintaxe. Com isso, você coloca o tratamento de null no C# 14 literalmente na palma das suas ?. mãos.
FAQ
O que é a atribuição condicional nula no C# 14?
É a possibilidade de usar ?. e ?[] do lado esquerdo de uma atribuição. A atribuição só acontece se o objeto não for nulo.
Preciso do .NET 10 para usar a atribuição condicional nula?
Sim, o recurso faz parte do C# 14, que é a versão da linguagem suportada pelo .NET 10. Basta usar o SDK do .NET 10.
O lado direito da atribuição é executado quando o objeto é nulo?
Não. O lado direito só é avaliado quando o receptor da expressão condicional não é nulo.
Posso usar ++ e -- com a nova sintaxe?
Não, incremento e decremento não são permitidos. Atribuições compostas como += e -= funcionam normalmente.
Os nullable reference types mudaram no C# 14?
Não. Eles seguem o mesmo modelo introduzido no C# 8 e ativado por padrão nos templates desde o .NET 6.
Quando não devo usar ?.=?
Evite quando o objeto nunca deveria ser nulo. Nesse caso, a sintaxe esconderia um bug que deveria gerar erro.