Tipos de referência anuláveis
Em um contexto anulável-alheio, todos os tipos de referência eram anuláveis. Tipos de referência anuláveis referem-se a um grupo de recursos habilitados em um contexto de reconhecimento anulável que minimizam a probabilidade de que seu código faça com que o tempo de execução seja lançado System.NullReferenceException. Os tipos de referência anuláveis incluem três recursos que ajudam a evitar essas exceções, incluindo a capacidade de marcar explicitamente um tipo de referência como anulável:
- Análise de fluxo estático aprimorada que determina se uma variável pode estar
null
antes de desreferenciar ela. - Atributos que anotam APIs para que a análise de fluxo determine o estado nulo.
- Anotações de variáveis que os desenvolvedores usam para declarar explicitamente o estado nulo pretendido para uma variável.
O compilador controla o estado nulo de cada expressão em seu código em tempo de compilação. O estado nulo tem um dos três valores:
- not-null: A expressão é conhecida por não ser-
null
. - maybe-null: A expressão pode ser
null
. - ignorante: O compilador não pode determinar o estado nulo da expressão.
As anotações de variáveis determinam a anulabilidade de uma variável de tipo de referência:
- non-nullable: Se você atribuir um
null
valor ou uma expressão talvez-nula à variável, o compilador emitirá um aviso. As variáveis que não são anuláveis têm um estado nulo padrão de não-nulo. - nullable: Você pode atribuir um
null
valor ou uma expressão talvez-nula à variável. Quando o estado nulo da variável é talvez-nulo, o compilador emite um aviso se você cancelar a referência da variável. O estado nulo padrão para a variável é talvez-nulo. - ignorante: Você pode atribuir um
null
valor ou uma expressão talvez-nula à variável. O compilador não emite avisos quando você desreferencia a variável ou quando atribui uma expressão talvez-nula à variável.
O estado nulo esquecido e a anulabilidade esquecida correspondem ao comportamento antes que os tipos de referência anuláveis fossem introduzidos. Esses valores são úteis durante a migração ou quando seu aplicativo usa uma biblioteca que não habilitou tipos de referência anuláveis.
A análise de estado nulo e as anotações de variáveis são desabilitadas por padrão para projetos existentes, o que significa que todos os tipos de referência continuam a ser anuláveis. A partir do .NET 6, eles são habilitados por padrão para novos projetos. Para obter informações sobre como habilitar esses recursos declarando um contexto de anotação anulável, consulte Contextos anuláveis.
O restante deste artigo descreve como essas três áreas de recursos funcionam para produzir avisos quando seu código pode estar desreferenciando um null
valor. Desreferenciar uma variável significa acessar um de seus membros usando o .
operador (ponto), como mostrado no exemplo a seguir:
string message = "Hello, World!";
int length = message.Length; // dereferencing "message"
Quando você cancela a referência de uma variável cujo valor é null
, o tempo de execução lança um System.NullReferenceExceptionarquivo .
Você aprenderá sobre:
- Análise de estado nulo do compilador: como o compilador determina se uma expressão não é nula ou talvez-nula.
- Atributos que são aplicados a APIs que fornecem mais contexto para a análise de estado nulo do compilador.
- Anotações de variáveis anuláveis que fornecem informações sobre sua intenção para variáveis. As anotações são úteis para campos para definir o estado nulo padrão no início dos métodos de membro.
- As regras que regem os argumentos de tipo genéricos. Novas restrições foram adicionadas porque os parâmetros de tipo podem ser tipos de referência ou tipos de valor. O sufixo
?
é implementado de forma diferente para tipos de valor anuláveis e tipos de referência anuláveis. - Contextos anuláveis ajudam a migrar grandes projetos. Você pode habilitar contextos ou avisos anuláveis em partes do seu aplicativo à medida que migra. Depois de abordar mais avisos, você pode habilitar tipos de referência anuláveis para todo o projeto.
Finalmente, você aprende as armadilhas conhecidas para a análise de estado nulo em struct
tipos e matrizes.
Você também pode explorar esses conceitos em nosso módulo Learn on Nullable safety in C#.
Análise de estado nulo
Quando os tipos de referência anuláveis são habilitados, a análise de estado nulo rastreia o estado nulo das referências. Uma expressão é não-nula ou talvez-nula. O compilador determina que uma variável não é nula de duas maneiras:
- A variável recebeu um valor que é conhecido por não ser nulo.
- A variável foi verificada e
null
não foi modificada desde essa verificação.
Quando os tipos de referência anuláveis não estão habilitados, todas as expressões têm o estado nulo de ignorante. O restante da seção descreve o comportamento quando tipos de referência anuláveis estão habilitados.
Qualquer variável que o compilador não tenha determinado como não-nula é considerada talvez-nula. A análise fornece avisos em situações em que você pode acidentalmente cancelar a referência de um null
valor. O compilador produz avisos com base no estado nulo.
- Quando uma variável não é nula, essa variável pode ser desreferenciada com segurança.
- Quando uma variável é talvez-nula, essa variável deve ser verificada para garantir que não
null
seja antes de desreferenciar.
Considere o seguinte exemplo:
string message = null;
// warning: dereference null.
Console.WriteLine($"The length of the message is {message.Length}");
var originalMessage = message;
message = "Hello, World!";
// No warning. Analysis determined "message" is not-null.
Console.WriteLine($"The length of the message is {message.Length}");
// warning!
Console.WriteLine(originalMessage.Length);
No exemplo anterior, o compilador determina que message
é talvez-nulo quando a primeira mensagem é impressa. Não há aviso para a segunda mensagem. A linha final de código produz um aviso porque originalMessage
pode ser null. O exemplo a seguir mostra um uso mais prático para atravessar uma árvore de nós até a raiz, processando cada nó durante a travessia:
void FindRoot(Node node, Action<Node> processNode)
{
for (var current = node; current != null; current = current.Parent)
{
processNode(current);
}
}
O código anterior não gera nenhum aviso para desreferenciar a variável current
. A análise estática determina que current
nunca é desreferenciado quando é talvez nulo. A variável current
é verificada antes current.Parent
null
de ser acessada e antes de passar current
para a ProcessNode
ação. Os exemplos anteriores mostram como o compilador determina o estado nulo para variáveis locais quando inicializado, atribuído ou comparado ao null
.
A análise de estado nulo não rastreia métodos chamados. Como resultado, os campos inicializados em um método auxiliar comum chamado por todos os construtores gera um aviso com o seguinte modelo:
A propriedade não anulável 'name' deve conter um valor não nulo ao sair do construtor.
Você pode abordar esses avisos de duas maneiras: encadeamento do construtor ou atributos anuláveis no método auxiliar. O código a seguir mostra um exemplo de cada um. A Person
classe usa um construtor comum chamado por todos os outros construtores. A Student
classe tem um método auxiliar anotado com o System.Diagnostics.CodeAnalysis.MemberNotNullAttribute atributo:
using System.Diagnostics.CodeAnalysis;
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public Person() : this("John", "Doe") { }
}
public class Student : Person
{
public string Major { get; set; }
public Student(string firstName, string lastName, string major)
: base(firstName, lastName)
{
SetMajor(major);
}
public Student(string firstName, string lastName) :
base(firstName, lastName)
{
SetMajor();
}
public Student()
{
SetMajor();
}
[MemberNotNull(nameof(Major))]
private void SetMajor(string? major = default)
{
Major = major ?? "Undeclared";
}
}
Nota
Uma série de melhorias para atribuição definida e análise de estado nulo foram adicionadas no C# 10. Quando você atualiza para C# 10, você pode encontrar menos avisos anuláveis que são falsos positivos. Você pode saber mais sobre as melhorias na especificação de recursos para melhorias de atribuição definidas.
A análise de estado anulável e os avisos que o compilador gera ajudam a evitar erros de programa desreferenciando null
. O artigo sobre como resolver avisos anuláveis fornece técnicas para corrigir os avisos provavelmente vistos em seu código.
Atributos em assinaturas de API
A análise de estado nulo precisa de dicas dos desenvolvedores para entender a semântica das APIs. Algumas APIs fornecem verificações nulas e devem alterar o estado nulo de uma variável de maybe-null para not-null. Outras APIs retornam expressões que não são nulas ou talvez-nulas , dependendo do estado nulo dos argumentos de entrada. Por exemplo, considere o seguinte código que exibe uma mensagem em maiúsculas:
void PrintMessageUpper(string? message)
{
if (!IsNull(message))
{
Console.WriteLine($"{DateTime.Now}: {message.ToUpper()}");
}
}
bool IsNull(string? s) => s == null;
Com base na inspeção, qualquer desenvolvedor consideraria esse código seguro e não deveria gerar avisos. No entanto, o compilador não sabe que IsNull
fornece uma verificação nula e emite um aviso para a message.ToUpper()
instrução, considerando message
ser uma variável talvez-nula . Use o NotNullWhen
atributo para corrigir este aviso:
bool IsNull([NotNullWhen(false)] string? s) => s == null;
Este atributo informa o compilador que, se IsNull
retornar false
, o parâmetro s
não é nulo. O compilador altera o estado nulo de message
para não-nulo dentro do if (!IsNull(message)) {...}
bloco . Nenhum aviso é emitido.
Os atributos fornecem informações detalhadas sobre o estado nulo de argumentos, valores de retorno e membros da instância de objeto usada para invocar um membro. Os detalhes sobre cada atributo podem ser encontrados no artigo de referência de linguagem sobre atributos de referência anuláveis. A partir do .NET 5, todas as APIs de tempo de execução do .NET são anotadas. Você melhora a análise estática anotando suas APIs para fornecer informações semânticas sobre o estado nulo de argumentos e valores de retorno.
Anotações de variáveis anuláveis
A análise de estado nulo fornece uma análise robusta para variáveis locais. O compilador precisa de mais informações suas para variáveis de membro. O compilador precisa de mais informações para definir o estado nulo de todos os campos no colchete de abertura de um membro. Qualquer um dos construtores acessíveis pode ser usado para inicializar o objeto. Se um campo membro pode ser definido como null
, o compilador deve assumir que seu estado nulo é talvez-nulo no início de cada método.
Você usa anotações que podem declarar se uma variável é um tipo de referência anulável ou um tipo de referência não anulável. Essas anotações fazem afirmações importantes sobre o estado nulo para variáveis:
- Uma referência não deve ser nula. O estado padrão de uma variável de referência não anulável é não-nulo. O compilador impõe regras que garantem que é seguro cancelar a referência dessas variáveis sem primeiro verificar se elas não são nulas:
- A variável deve ser inicializada com um valor não nulo.
- A variável nunca pode ser atribuída o valor
null
. O compilador emite um aviso quando o código atribui uma expressão talvez-nula a uma variável que não deveria ser nula.
- Uma referência pode ser nula. O estado padrão de uma variável de referência anulável é talvez-nulo. O compilador impõe regras para garantir que você verifique corretamente se há uma
null
referência:- A variável só pode ser desreferenciada quando o compilador pode garantir que o valor não
null
é . - Essas variáveis podem ser inicializadas com o valor padrão
null
e podem receber o valornull
em outro código. - O compilador não emite avisos quando o código atribui uma expressão talvez-nula a uma variável que pode ser nula.
- A variável só pode ser desreferenciada quando o compilador pode garantir que o valor não
Qualquer variável de referência não anulável tem um estado nulo padrão de não-nulo. Qualquer variável de referência anulável tem o estado nulo inicial de maybe-null.
Um tipo de referência anulável é anotado usando a mesma sintaxe que os tipos de valor anulável: a ?
é acrescentado ao tipo da variável. Por exemplo, a seguinte declaração de variável representa uma variável de cadeia de caracteres anulável, name
:
string? name;
Quando os tipos de referência anuláveis são habilitados, qualquer variável em que o ?
não é anexado ao nome do tipo é um tipo de referência não anulável. Isso inclui todas as variáveis de tipo de referência no código existente, uma vez que você ativar esse recurso. No entanto, quaisquer variáveis locais digitadas implicitamente (declaradas usando var
) são tipos de referência anuláveis. Como as seções anteriores mostraram, a análise estática determina o estado nulo das variáveis locais para determinar se elas são talvez nulas antes de desreferenciar.
Às vezes, você deve substituir um aviso quando sabe que uma variável não é nula, mas o compilador determina que seu estado nulo é talvez-nulo. Você usa o operador !
null-forgiving seguindo um nome de variável para forçar o estado nulo a ser não-nulo. Por exemplo, se você sabe que a name
variável não null
é, mas o compilador emite um aviso, você pode escrever o seguinte código para substituir a análise do compilador:
name!.Length;
Tipos de referência anuláveis e tipos de valor anulável fornecem um conceito semântico semelhante: uma variável pode representar um valor ou objeto, ou essa variável pode ser null
. No entanto, tipos de referência anuláveis e tipos de valor anulável são implementados de forma diferente: tipos de valor anuláveis são implementados usando System.Nullable<T>, e tipos de referência anuláveis são implementados por atributos lidos pelo compilador. Por exemplo, string?
e string
ambos são representados pelo mesmo tipo: System.String. No entanto, int?
e int
são representados por System.Nullable<System.Int32>
e System.Int32, respectivamente.
Os tipos de referência anuláveis são um recurso de tempo de compilação. Isso significa que é possível que os chamadores ignorem avisos, usando null
intencionalmente como argumento para um método que espera uma referência não anulável. Os autores da biblioteca devem incluir verificações em tempo de execução em relação a valores de argumento nulos. A ArgumentNullException.ThrowIfNull é a opção preferida para verificar um parâmetro contra null em tempo de execução.
Importante
Habilitar anotações anuláveis pode alterar como o Entity Framework Core determina se um membro de dados é necessário. Você pode saber mais detalhes no artigo sobre Fundamentos principais do Entity Framework: trabalhando com tipos de referência anuláveis.
Genéricos
Os genéricos requerem regras detalhadas para lidar com T?
qualquer parâmetro T
de tipo. As regras são necessariamente detalhadas devido ao histórico e à implementação diferente para um tipo de valor anulável e um tipo de referência anulável. Os tipos de valor anulável são implementados usando o System.Nullable<T> struct. Tipos de referência anuláveis são implementados como anotações de tipo que fornecem regras semânticas para o compilador.
- Se o argumento type for
T
for um tipo de referência,T?
fará referência ao tipo de referência anulável correspondente. Por exemplo, seT
é umstring
, entãoT?
é umstring?
. - Se o argumento type for
T
for um tipo de valor,T?
fará referência ao mesmo tipo de valor,T
. Por exemplo, seT
é umint
, oT?
também é umint
. - Se o argumento type for
T
for um tipo de referência anulável,T?
fará referência a esse mesmo tipo de referência anulável. Por exemplo, seT
é umstring?
, entãoT?
também é umstring?
. - Se o argumento type for
T
for um tipo de valor anulável,T?
fará referência a esse mesmo tipo de valor anulável. Por exemplo, seT
é umint?
, entãoT?
também é umint?
.
Para valores de retorno, T?
é equivalente a [MaybeNull]T
; para valores de argumento, T?
é equivalente a [AllowNull]T
. Para obter mais informações, consulte o artigo sobre Atributos para análise de estado nulo na referência de idioma.
Você pode especificar comportamentos diferentes usando restrições:
- A
class
restrição significa queT
deve ser um tipo de referência não anulável (por exemplostring
, ). O compilador produz um aviso se você usar um tipo de referência anulável, comostring?
paraT
. - A
class?
restrição significa queT
deve ser um tipo de referência, não anulável (string
) ou um tipo de referência anulável (por exemplostring?
). Quando o parâmetro type é um tipo de referência anulável, comostring?
, uma expressão de referências desse mesmo tipo deT?
referência anulável, comostring?
. - A
notnull
restrição significa queT
deve ser um tipo de referência não anulável ou um tipo de valor não anulável. Se você usar um tipo de referência anulável ou um tipo de valor anulável para o parâmetro type, o compilador produzirá um aviso. Além disso, quandoT
é um tipo de valor, o valor de retorno é esse tipo de valor, não o tipo de valor anulável correspondente.
Essas restrições ajudam a fornecer mais informações ao compilador sobre como T
é usado. Isso ajuda quando os desenvolvedores escolhem o tipo para T
e fornece uma melhor análise de estado nulo quando uma instância do tipo genérico é usada.
Contextos anuláveis
Para projetos pequenos, você pode habilitar tipos de referência anuláveis, corrigir avisos e continuar. No entanto, para projetos maiores e soluções multiprojeto, isso pode gerar um grande número de avisos. Você pode usar pragmas para habilitar tipos de referência anuláveis arquivo por arquivo quando começar a usar tipos de referência anuláveis. Os novos recursos que protegem contra o lançamento de um System.NullReferenceException podem causar interrupções quando ativados em uma base de código existente:
- Todas as variáveis de referência explicitamente tipadas são interpretadas como tipos de referência não anuláveis.
- O significado da
class
restrição em genéricos mudou para significar um tipo de referência não anulável. - Novos avisos são gerados por causa dessas novas regras.
O contexto de anotação anulável determina o comportamento do compilador. Há quatro valores para o contexto de anotação anulável:
- disable: O código é anulável-esquecido. Desabilitar corresponde ao comportamento antes que os tipos de referência anuláveis fossem habilitados, exceto que a nova sintaxe produz avisos em vez de erros.
- Os avisos anuláveis estão desativados.
- Todas as variáveis de tipo de referência são tipos de referência anuláveis.
- O uso do sufixo
?
para declarar um tipo de referência anulável produz um aviso. - Você pode usar o operador de perdão nulo,
!
mas ele não tem efeito.
- enable: O compilador permite todas as análises de referência nulas e todos os recursos de linguagem.
- Todos os novos avisos anuláveis estão habilitados.
- Você pode usar o sufixo
?
para declarar um tipo de referência anulável. - As variáveis de tipo de referência sem o sufixo
?
são tipos de referência não anuláveis. - O operador de perdão nulo suprime avisos para uma possível atribuição a
null
.
- avisos: O compilador executa toda a análise nula e emite avisos quando o código pode cancelar a referência
null
.- Todos os novos avisos anuláveis estão habilitados.
- O uso do sufixo
?
para declarar um tipo de referência anulável produz um aviso. - Todas as variáveis de tipo de referência podem ser nulas. No entanto, os membros têm o estado nulo de não-nulo na chave de abertura de todos os métodos, a menos que declarado com o sufixo
?
. - Você pode usar o operador de perdão nulo,
!
.
- anotações: O compilador não emite avisos quando o código pode cancelar a referência
null
ou quando você atribui uma expressão talvez-nula a uma variável não anulável.- Todos os novos avisos anuláveis estão desativados.
- Você pode usar o sufixo
?
para declarar um tipo de referência anulável. - As variáveis de tipo de referência sem o sufixo
?
são tipos de referência não anuláveis. - Você pode usar o operador de perdão nulo,
!
mas ele não tem efeito.
O contexto de anotação anulável e o contexto de aviso anulável podem ser definidos para um projeto usando o <Nullable>
elemento no arquivo .csproj . Este elemento configura como o compilador interpreta a anulabilidade de tipos e quais avisos são emitidos. A tabela a seguir mostra os valores permitidos e resume os contextos que eles especificam.
Contexto | Advertências de cancelamento de referência | Avisos de atribuição | Tipos de referência | ? sufixo |
! Operador |
---|---|---|---|---|---|
disable |
Disabled | Disabled | Todos são anuláveis | Produz um aviso | Não tem efeito |
enable |
Ativados | Ativados | Não anulável, a menos que declarado com ? |
Declara o tipo nulo | Suprime avisos para possível null atribuição |
warnings |
Ativado(a) | Não aplicável | Todos são anuláveis, mas os membros são considerados não-nulos na chave de abertura dos métodos | Produz um aviso | Suprime avisos para possível null atribuição |
annotations |
Disabled | Disabled | Não anulável, a menos que declarado com ? |
Declara o tipo nulo | Não tem efeito |
As variáveis de tipo de referência no código compilado em um contexto desabilitado são anuláveis-ignorantes. Você pode atribuir uma null
variável literal ou talvez-nula a uma variável que seja anulável-ignorante. No entanto, o estado padrão de uma variável anulável-esquecida não é nulo.
Você pode escolher qual configuração é melhor para o seu projeto:
- Escolha desativar para projetos herdados que você não deseja atualizar com base em diagnósticos ou novos recursos.
- Escolha avisos para determinar onde seu código pode lançar System.NullReferenceExceptions. Você pode abordar esses avisos antes de modificar o código para habilitar tipos de referência não anuláveis.
- Escolha anotações para expressar sua intenção de design antes de ativar os avisos.
- Escolha habilitar para novos projetos e projetos ativos onde você deseja proteger contra exceções de referência nula.
Exemplo:
<Nullable>enable</Nullable>
Você também pode usar diretivas para definir esses mesmos contextos em qualquer lugar do código-fonte. Essas diretivas são mais úteis quando você está migrando uma base de código grande.
#nullable enable
: Define o contexto de anotação anulável e o contexto de aviso anulável para habilitar.#nullable disable
: Define o contexto de anotação anulável e o contexto de aviso anulável como desabilitável.#nullable restore
: Restaura o contexto de anotação anulável e o contexto de aviso anulável para as configurações do projeto.#nullable disable warnings
: Defina o contexto de aviso anulável como desativado.#nullable enable warnings
: Defina o contexto de aviso anulável para habilitar.#nullable restore warnings
: Restaura o contexto de aviso nulo para as configurações do projeto.#nullable disable annotations
: Defina o contexto de anotação anulável como desabilitável.#nullable enable annotations
: Defina o contexto de anotação anulável para habilitar.#nullable restore annotations
: Restaura o contexto de aviso de anotação para as configurações do projeto.
Para qualquer linha de código, você pode definir qualquer uma das seguintes combinações:
Contexto de aviso | Contexto da anotação | Utilizar |
---|---|---|
padrão do projeto | padrão do projeto | Predefinido |
ativar | desativar | Corrigir avisos de análise |
ativar | padrão do projeto | Corrigir avisos de análise |
padrão do projeto | ativar | Adicionar anotações de tipo |
ativar | ativar | Código já migrado |
desativar | ativar | Anotar código antes de corrigir avisos |
desativar | desativar | Adicionando código herdado ao projeto migrado |
padrão do projeto | desativar | Raramente |
desativar | padrão do projeto | Raramente |
Essas nove combinações fornecem um controle refinado sobre os diagnósticos que o compilador emite para seu código. Pode ativar mais funcionalidades em qualquer área que esteja a atualizar, sem ver mais avisos que ainda não está pronto para abordar.
Importante
O contexto global anulável não se aplica aos arquivos de código gerados. Em qualquer uma das estratégias, o contexto anulável é desabilitado para qualquer arquivo de origem marcado como gerado. Isso significa que todas as APIs nos arquivos gerados não são anotadas. Há quatro maneiras pelas quais um arquivo é marcado como gerado:
- No .editorconfig, especifique
generated_code = true
em uma seção que se aplica a esse arquivo. - Coloque
<auto-generated>
ou<auto-generated/>
em um comentário na parte superior do arquivo. Ele pode estar em qualquer linha nesse comentário, mas o bloco de comentários deve ser o primeiro elemento no arquivo. - Inicie o nome do arquivo com TemporaryGeneratedFile_
- Termine o nome do arquivo com .designer.cs, .generated.cs, .g.cs ou .g.i.cs.
Os geradores podem optar por participar usando a diretiva de #nullable
pré-processador.
Por padrão, a anotação anulável e os contextos de aviso são desabilitados. Isso significa que seu código existente é compilado sem alterações e sem gerar novos avisos. A partir do .NET 6, novos projetos incluem o <Nullable>enable</Nullable>
elemento em todos os modelos de projeto.
Essas opções fornecem duas estratégias distintas para atualizar uma base de código existente para usar tipos de referência anuláveis.
Armadilhas conhecidas
Matrizes e estruturas que contêm tipos de referência são armadilhas conhecidas em referências anuláveis e na análise estática que determina segurança nula. Em ambas as situações, uma referência não anulável pode ser inicializada para null
, sem gerar avisos.
Estruturas
Uma struct que contém tipos de referência não anuláveis permite atribuí-la default
sem nenhum aviso. Considere o seguinte exemplo:
using System;
#nullable enable
public struct Student
{
public string FirstName;
public string? MiddleName;
public string LastName;
}
public static class Program
{
public static void PrintStudent(Student student)
{
Console.WriteLine($"First name: {student.FirstName.ToUpper()}");
Console.WriteLine($"Middle name: {student.MiddleName?.ToUpper()}");
Console.WriteLine($"Last name: {student.LastName.ToUpper()}");
}
public static void Main() => PrintStudent(default);
}
No exemplo anterior, não há nenhum aviso em PrintStudent(default)
enquanto os tipos FirstName
de referência não anuláveis e LastName
são nulos.
Outro caso mais comum é quando você lida com estruturas genéricas. Considere o seguinte exemplo:
#nullable enable
public struct S<T>
{
public T Prop { get; set; }
}
public static class Program
{
public static void Main()
{
string s = default(S<string>).Prop;
}
}
No exemplo anterior, a propriedade Prop
está null
em tempo de execução. Ele é atribuído a uma cadeia de caracteres não anulável sem nenhum aviso.
Matrizes
As matrizes também são uma armadilha conhecida em tipos de referência anuláveis. Considere o exemplo a seguir que não produz nenhum aviso:
using System;
#nullable enable
public static class Program
{
public static void Main()
{
string[] values = new string[10];
string s = values[0];
Console.WriteLine(s.ToUpper());
}
}
No exemplo anterior, a declaração da matriz mostra que ela contém cadeias de caracteres não anuláveis, enquanto seus elementos são todos inicializados em null
. Em seguida, é atribuído um null
valor à variável s
(o primeiro elemento da matriz). Finalmente, a variável s
é desreferenciada causando uma exceção de tempo de execução.