Tipos que permitem valor nulo (referência do C#)

Um tipo de valor anulávelT? representa todos os valores do tipo de valorT subjacente e um valor null adicional. Por exemplo, você pode atribuir qualquer um dos seguintes três valores a uma variável bool?: true, false ou null. Um tipo de valor T subjacente não pode ser um tipo de valor anulável em si.

Qualquer tipo de valor anulável é uma instância da estrutura genérica System.Nullable<T>. Você pode se referir a um tipo de valor anulável com um tipo subjacente T em qualquer um dos seguintes formulários intercambiáveis: Nullable<T> ou T?.

Normalmente, você usa um tipo de valor anulável quando precisa representar o valor indefinido de um tipo de valor subjacente. Por exemplo, uma variável booliana ou bool, só pode ser true ou false. No entanto, em alguns aplicativos, um valor variável pode estar indefinido ou ausente. Por exemplo, um campo de dados pode conter true ou false, ou pode não conter nenhum valor, ou seja, NULL. Você pode usar o tipo bool? nesse cenário.

Declaração e atribuição

Como um tipo de valor é implicitamente conversível para o tipo de valor anulável correspondente, você pode atribuir um valor a uma variável de um tipo de valor anulável, como faria com o tipo de valor subjacente. Você também pode atribuir o valor null. Por exemplo:

double? pi = 3.14;
char? letter = 'a';

int m2 = 10;
int? m = m2;

bool? flag = null;

// An array of a nullable value type:
int?[] arr = new int?[10];

O valor padrão de um tipo de valor anulável representa null, ou seja, é uma instância cuja propriedade Nullable<T>.HasValue retorna false.

Exame de uma instância de um tipo de valor anulável

Você pode usar o operador is com um padrão de tipo para examinar uma instância de um tipo de valor anulável para null e recuperar um valor de um tipo subjacente:

int? a = 42;
if (a is int valueOfA)
{
    Console.WriteLine($"a is {valueOfA}");
}
else
{
    Console.WriteLine("a does not have a value");
}
// Output:
// a is 42

Você sempre pode usar as seguintes propriedades somente leitura para examinar e obter um valor de uma variável de tipo de valor anulável:

O seguinte exemplo usa a propriedade HasValue para testar se a variável contém um valor antes de exibi-lo:

int? b = 10;
if (b.HasValue)
{
    Console.WriteLine($"b is {b.Value}");
}
else
{
    Console.WriteLine("b does not have a value");
}
// Output:
// b is 10

Você também pode comparar uma variável de um tipo de valor anulável com null em vez de usar a propriedade HasValue, como mostra o seguinte exemplo:

int? c = 7;
if (c != null)
{
    Console.WriteLine($"c is {c.Value}");
}
else
{
    Console.WriteLine("c does not have a value");
}
// Output:
// c is 7

Conversão de um tipo de valor anulável para um tipo subjacente

Se você quiser atribuir um valor de um tipo de valor anulável a uma variável de tipo de valor não anulável, talvez seja necessário especificar o valor a ser atribuído no lugar de null. Use o operador de avaliação de nulo ?? para fazer isso (você também pode usar o método Nullable<T>.GetValueOrDefault(T) para a mesma finalidade):

int? a = 28;
int b = a ?? -1;
Console.WriteLine($"b is {b}");  // output: b is 28

int? c = null;
int d = c ?? -1;
Console.WriteLine($"d is {d}");  // output: d is -1

Se quiser usar o valor padrão do tipo de valor subjacente no lugar de null, use o método Nullable<T>.GetValueOrDefault().

Você também pode converter explicitamente um tipo de valor anulável em um tipo não anulável, como mostra o seguinte exemplo:

int? n = null;

//int m1 = n;    // Doesn't compile
int n2 = (int)n; // Compiles, but throws an exception if n is null

Em tempo de execução, se o valor de um tipo de valor anulável for null, a conversão explícita vai gerar uma InvalidOperationException.

Um tipo de valor não anulável T é implicitamente conversível para o tipo de valor anulável correspondente T?.

Operadores suspensos

Os operadores unários e binários predefinidos ou quaisquer operadores sobrecarregados com suporte por um tipo de valor T também têm suporte pelo tipo de valor anulável correspondente T?. Esses operadores, também conhecidos como operadores suspensos, vão gerar null se um ou ambos os operadores forem null; caso contrário, o operador usará os valores contidos dos operadores dele para calcular o resultado. Por exemplo:

int? a = 10;
int? b = null;
int? c = 10;

a++;        // a is 11
a = a * c;  // a is 110
a = a + b;  // a is null

Observação

Para o tipo bool?, os operadores predefinidos & e | não seguirão as regras descritas nessa seção: o resultado de uma avaliação do operador poderá ser não nulo, mesmo quando um dos operandos for null. Para obter mais informações, confira a seção Operadores lógicos booleanos anuláveis do artigo Operadores lógicos boolianos.

Para os operadores de comparação<, >, <= e >=, se um ou ambos os operandos forem null, o resultado será false; caso contrário, os valores contidos de operandos serão comparados. Não presuma que como uma comparação (por exemplo, <=) retorna false, a comparação oposta (>) retorna true. O exemplo a seguir mostra que 10

  • nem maior ou igual a null
  • nem menor que null
int? a = 10;
Console.WriteLine($"{a} >= null is {a >= null}");
Console.WriteLine($"{a} < null is {a < null}");
Console.WriteLine($"{a} == null is {a == null}");
// Output:
// 10 >= null is False
// 10 < null is False
// 10 == null is False

int? b = null;
int? c = null;
Console.WriteLine($"null >= null is {b >= c}");
Console.WriteLine($"null == null is {b == c}");
// Output:
// null >= null is False
// null == null is True

Para o operador de igualdade==, se ambos os operandos forem null, o resultado será true, se apenas um dos operandos for null, o resultado será false; caso contrário, os valores contidos dos operandos serão comparados.

Para o operador de desigualdade!=, se ambos os operandos forem null, o resultado será false, se apenas um dos operandos for null, o resultado será true; caso contrário, os valores contidos dos operandos serão comparados.

Se houver uma conversão definida pelo usuário entre dois tipos de valor, a mesma conversão também poderá ser usada entre os tipos de valor anuláveis correspondentes.

Conversão boxing e unboxing

Uma instância de um tipo de valor anulável T? é demarcada da seguinte maneira:

  • Se HasValue retorna false, a referência nula é produzida.
  • Se HasValue retornar true, o valor correspondente do tipo de valor subjacente T será demarcado, não a instância de Nullable<T>.

Você pode desfazer um valor demarcado de um tipo de valor T para o tipo de valor anulável correspondente T?, como mostra o seguinte exemplo:

int a = 41;
object aBoxed = a;
int? aNullable = (int?)aBoxed;
Console.WriteLine($"Value of aNullable: {aNullable}");

object aNullableBoxed = aNullable;
if (aNullableBoxed is int valueOfA)
{
    Console.WriteLine($"aNullableBoxed is boxed int: {valueOfA}");
}
// Output:
// Value of aNullable: 41
// aNullableBoxed is boxed int: 41

Como identificar um tipo de valor anulável

O seguinte exemplo mostra como determinar se uma instância System.Type representa um tipo de valor anulável construído, ou seja, o tipo System.Nullable<T> com um parâmetro de tipo especificado T:

Console.WriteLine($"int? is {(IsNullable(typeof(int?)) ? "nullable" : "non nullable")} value type");
Console.WriteLine($"int is {(IsNullable(typeof(int)) ? "nullable" : "non-nullable")} value type");

bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;

// Output:
// int? is nullable value type
// int is non-nullable value type

Como mostra o exemplo, você usa o operador typeof para criar uma instância System.Type.

Se você quiser determinar se uma instância é de um tipo de valor anulado, não use o método Object.GetType para fazer com que uma instância Type seja testada com o código anterior. Quando você chama o método Object.GetType em uma instância de um tipo de valor anulável, a instância é demarcada para Object. Como a conversão boxing de uma instância não nula de um tipo de valor anulável é equivalente à conversão boxing de um valor do tipo subjacente, GetType retorna uma instância Type que representa o tipo subjacente de um tipo de valor anulável:

int? a = 17;
Type typeOfA = a.GetType();
Console.WriteLine(typeOfA.FullName);
// Output:
// System.Int32

Além disso, não use o operador is para determinar se uma instância é de um tipo de valor anulável. Como mostra o seguinte exemplo, não é possível distinguir tipos de uma instância de tipo de valor anulável e a instância de tipo subjacente dela com o operador is:

int? a = 14;
if (a is int)
{
    Console.WriteLine("int? instance is compatible with int");
}

int b = 17;
if (b is int?)
{
    Console.WriteLine("int instance is compatible with int?");
}
// Output:
// int? instance is compatible with int
// int instance is compatible with int?

Em vez disso, use o operador Nullable.GetUnderlyingType do primeiro exemplo e o operador typeof para verificar se uma instância é de um tipo de valor anulável.

Observação

Os métodos descritos nesta seção não são aplicáveis no caso de tipos de referência anuláveis.

Especificação da linguagem C#

Para obter mais informações, confira as seguintes seções da especificação da linguagem C#:

Confira também