Tipos de valor que admiten valores NULL (referencia de C#)

Un tipo de valor que acepta valores NULLT? representa todos los valores de su tipo de valorT subyacente y un valor null adicional. Por ejemplo, puede asignar cualquiera de los tres valores siguientes a una variable bool?: true, false o null. Un tipo de valor subyacente T no puede ser un tipo de valor que acepte valores NULL por sí mismo.

Todos los tipos que admiten valores NULL son instancias de la estructura System.Nullable<T> genérica. Puede hacer referencia a un tipo de valor que admite valores NULL con un tipo subyacente T en cualquiera de las formas intercambiables siguientes: Nullable<T> o T?.

Normalmente, los tipos de valor que admiten valores NULL se usan cuando es necesario representar el valor indefinido de un tipo de valor subyacente. Por ejemplo, una variable booleana, o bool, solo puede ser true o false. Sin embargo, en algunas aplicaciones, un valor de variable puede estar sin definir o faltar. Por ejemplo, un campo de base de datos puede contener true o false, o puede no contener ningún valor, es decir, NULL. Puede usar el tipo bool? en ese escenario.

Declaración y asignación

Como un tipo de valor se puede convertir de forma implícita al tipo de valor que admite valores NULL correspondiente, un valor se puede asignar a un tipo que admite valores NULL como se haría para su tipo de valor subyacente. También se puede asignar el valor null. Por ejemplo:

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];

El valor predeterminado de un tipo de valor que admite valores NULL se representa como null, es decir, es una instancia cuya propiedad Nullable<T>.HasValue devuelve false.

Examen de una instancia de tipo de valor que admite valores NULL

Puede usar el operador is con un patrón de tipo para examinar una instancia de un tipo de valor que admita valores NULL para null y recuperar un valor de un tipo subyacente:

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

Siempre puede usar las siguientes propiedades de solo lectura para examinar y obtener un valor de una variable de tipo de valor que admite valores NULL:

En el ejemplo siguiente se usa la propiedad HasValue para comprobar si la variable contiene un valor antes de mostrarlo:

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

También se puede comparar una variable de un tipo de valor que admite valores NULL con null en lugar de usar la propiedad HasValue, como se muestra en el ejemplo siguiente:

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

Conversión de un tipo de valor que admite un valor NULL a un tipo subyacente

Si desea asignar un valor de un tipo que admite valores NULL a una variable de tipo de valor que no admite valores NULL, puede que tenga que especificar el valor que se va a asignar en lugar de null. Use el operador de fusión de NULL ?? para ello (también puede usar el método Nullable<T>.GetValueOrDefault(T) con el mismo fin):

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

Si desea utilizar el valor predeterminado del tipo de valor subyacente en lugar de null, use el método Nullable<T>.GetValueOrDefault().

También puede convertir de forma explícita un tipo de valor que admite valores NULL en uno que no los admite, como se indica en el ejemplo siguiente:

int? n = null;

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

En tiempo de ejecución, si el valor de un tipo de valor que admite un valor NULL es null, la conversión explícita inicia una InvalidOperationException.

Un tipo de valor que no admite valores NULL T se convierte implícitamente al tipo que admite valores NULL T? correspondiente.

Operadores de elevación

Los operadores unarios y binarios predefinidos o los operadores sobrecargados que admite un tipo de valor T también se admiten en el tipo de valor que admite un valor NULL T? correspondiente. Estos operadores, también conocidos como operadores de elevación, generan un valor null si uno o los dos operandos son null; de lo contrario, el operador usa los valores contenidos de sus operandos para calcular el resultado. Por ejemplo:

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 el tipo bool?, los operadores predefinidos & y | no siguen las reglas descritas en esta sección: el resultado de una evaluación de operador puede ser distinto de NULL incluso si uno de los operandos es null. Para más información, consulte la sección Operadores lógicos booleanos que aceptan valores NULL del artículo Operadores lógicos booleanos.

En el caso de los operadores de comparación<, >, <= y >=, si uno o ambos operandos son null, el resultado es false; de lo contrario, se comparan los valores contenidos de los operandos. No asuma que, como una comparación determinada (por ejemplo, <=) devuelve false, la comparación contraria (>) devolverá true. En el ejemplo siguiente se muestra que 10 no es

  • mayor ni igual que null,
  • ni es 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

En el caso de los operadores de igualdad==, si ambos operandos son null, el resultado es true; si solo uno de los operandos es null, el resultado es false; de lo contrario, se comparan los valores contenidos de los operandos.

En el caso de los operadores de desigualdad!=, si ambos operandos son null, el resultado es false; si solo uno de los operandos es null, el resultado es true; de lo contrario, se comparan los valores contenidos de los operandos.

Si existe una conversión definida por el usuario entre dos tipos de valor, esa misma conversión también se puede usar entre los correspondientes tipos que aceptan valores NULL.

Conversión boxing y conversión unboxing

La conversión boxing de una instancia de un tipo de valor que acepta valores NULL T? se realiza de la siguiente manera:

  • Si HasValue devuelve false, se genera la referencia nula.
  • Si HasValue devuelve true, se aplica la conversión boxing al correspondiente valor del tipo de valor subyacente T, no a la instancia de Nullable<T>.

Se puede aplicar conversión unboxing a un tipo de valor T con conversión boxing al tipo T? que acepta valores NULL correspondiente, como se muestra en el ejemplo siguiente:

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

Identificación de tipos que admiten valores NULL

El ejemplo siguiente muestra cómo determinar si una instancia System.Type representa un tipo de valor construido que admite valores NULL, es decir, el tipo System.Nullable<T> con un 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 se muestra en el ejemplo, se usa el operador typeof para crear una instancia System.Type.

Si quiere determinar si una instancia es de un tipo de valor que admite un valor NULL, no use el método Object.GetType para obtener una instancia de Type para probarla con el código anterior. Cuando se llama al método Object.GetType en una instancia de un tipo de valor que admite un valor NULL, se aplica la conversión boxing a la instancia para convertirla en Object. Como la conversión boxing de una instancia que no es NULL de un tipo de valor que admite valores NULL equivale a la conversión boxing de un valor del tipo subyacente, GetType devuelve una instancia Type que representa el tipo de valor subyacente que admite valores NULL:

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

No use el operador is para determinar si una instancia es de un tipo de valor que admite valores NULL. Como se muestra en el ejemplo siguiente, no se pueden distinguir los tipos de instancias de un tipo de valor que admite valores NULL y su tipo subyacente mediante el 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?

En su lugar, use el Nullable.GetUnderlyingType del primer ejemplo y el operador typeof para comprobar si una instancia es de un tipo de valor que acepte valores NULL.

Nota

Los métodos que se describen en esta sección no son aplicables en el caso de los tipos de referencia nula.

Especificación del lenguaje C#

Para más información, vea las secciones siguientes de la Especificación del lenguaje C#:

Consulte también