Tipos de valor anulável (referência C#)

Um tipoT? de valor anulável representa todos os valores de seu tipoT de valor subjacente e um valor nulo adicional. Por exemplo, você pode atribuir qualquer um dos três valores a seguir a uma bool? variável: true, false, ou null. Um tipo T de valor 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 fazer referência a um tipo de valor anulável com um tipo T subjacente em qualquer uma das seguintes formas 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 booleana, ou bool, só pode ser uma true ou false. No entanto, em alguns aplicativos, um valor de variável pode estar indefinido ou ausente. Por exemplo, um campo de banco de dados pode conter true ou false, ou pode não conter nenhum valor, ou seja, NULL. Você pode usar o bool? tipo nesse cenário.

Declaração e cessã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 para seu tipo de valor subjacente. Você também pode atribuir o null valor. 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 Nullable<T>.HasValue propriedade retorna false.

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

Você pode usar o is operador com um padrão de tipo para examinar uma instância de um tipo de valor anulável e null 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 exemplo a seguir usa a HasValue propriedade 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 HasValue propriedade, como mostra o exemplo a seguir:

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 desejar 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 ?? null-coalescing para fazer isso (você também pode usar o Nullable<T>.GetValueOrDefault(T) método 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 você quiser usar o valor padrão do tipo de valor subjacente no lugar de null, use o Nullable<T>.GetValueOrDefault() método.

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

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, o cast explícito lançará um InvalidOperationExceptionarquivo .

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

Operadores de elevadores

Os operadores unários e binários predefinidos ou quaisquer operadores sobrecarregados que são suportados por um tipo T de valor também são suportados pelo tipo T?de valor anulável correspondente. Esses operadores, também conhecidos como operadores levantados, produzem null se um ou ambos os operandos são null, caso contrário, o operador usa os valores contidos de seus operandos 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

Nota

Para o bool? tipo, os operadores predefinidos & e | não seguem as regras descritas nesta seção: o resultado de uma avaliação do operador pode ser não-nulo, mesmo que um dos operandos seja null. Para obter mais informações, consulte a seção Operadores lógicos booleanos anuláveis do artigo Operadores lógicos booleanos.

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

  • nem superior nem igual a null
  • nem inferior a 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 são null, o resultado é true, se apenas um dos operandos é null, o resultado é false; caso contrário, os valores contidos dos operandos são comparados.

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

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

Boxe e unboxing

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

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

Você pode descompactar um valor em caixa de um tipo T de valor para o tipo T?de valor anulável correspondente, como mostra o exemplo a seguir:

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 exemplo a seguir mostra como determinar se uma System.Type instância representa um tipo de valor nulo construído, ou seja, o System.Nullable<T> tipo com um parâmetro Ttype especificado:

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 System.Type instância.

Se você quiser determinar se uma instância é de um tipo de valor anulável, não use o Object.GetType método para obter uma Type instância a ser testada com o código anterior. Quando você chama o Object.GetType método em uma instância de um tipo de valor anulável, a instância é encaixotada como Object. Como o boxing de uma instância não nula de um tipo de valor anulável é equivalente ao boxing de um valor do tipo subjacente, GetType retorna uma Type instância 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 exemplo a seguir, não é possível distinguir tipos de uma instância de tipo de valor anulável e sua instância de tipo subjacente com o is operador:

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 Nullable.GetUnderlyingType operador from the first example e typeof para verificar se uma instância é de um tipo de valor anulável.

Nota

Os métodos descritos nesta secçã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, consulte as seguintes seções da especificação da linguagem C#:

Consulte também