Tipos que permitem valor nulo (referência do C#)
Um tipo de valor anulável T?
representa todos os valores do tipo de valor T
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:
Nullable<T>.HasValue indica se uma instância de um tipo de valor anulável tem um valor do tipo subjacente dela.
Nullable<T>.Value obtém o valor de um tipo subjacente quando HasValue é
true
. Quando HasValue éfalse
, a propriedade Value gera uma InvalidOperationException.
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 subjacenteT
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#:
- Tipos anuláveis
- Operadores suspensos
- Conversões anuláveis implícitas
- Conversões anuláveis explícitas
- Operadores de conversão suspensos