Utilizar tipos que aceptan valores NULL (Guía de programación de C#)

Los tipos que aceptan valores NULL pueden representar todos los valores de un tipo subyacente y un valor null adicional. Los tipos que aceptan valores NULL se declaran de dos formas:

System.Nullable<T> variable

O bien

T? variable

T es el tipo subyacente del tipo que acepta valores NULL. T puede ser cualquier tipo de valor incluido struct; no puede ser tipo de referencia.

Para obtener un ejemplo de cuándo se podría utilizar un tipo que acepta valores NULL, piense en una variable booleana común que puede tener dos valores: true y false. No hay ningún valor que signifique "indefinido". En muchas aplicaciones de programación, en particular en las interacciones de bases de datos, pueden existir variables con un estado indefinido. Por ejemplo, un campo de una base de datos puede contener los valores true o false, pero también puede que no contenga ningún valor. Igualmente, los tipos de referencia se pueden establecer en null para indicar que no se inicializan.

Esta disparidad puede crear trabajo de programación extra, con variables adicionales que se utilizan para almacenar información de estado, el uso de valores especiales, etc. El modificador de tipos que aceptan valores NULL permite a C# crear variables de tipo de valor que pueden indicar un valor indefinido.

Ejemplos de tipos que aceptan valores NULL

Cualquier tipo de valor se puede utilizar como base para un tipo que acepta valores NULL. Por ejemplo:

int? i = 10;
double? d1 = 3.14;
bool? flag = null;
char? letter = 'a';
int?[] arr = new int?[10];

Miembros de tipos que aceptan valores NULL

Cada instancia de un tipo que acepta valores NULL tiene dos propiedades públicas de sólo lectura:

  • HasValue

    HasValue es de tipo bool. Se establece como true cuando la variable contiene un valor que no es null.

  • Value

    Value es del mismo tipo que el tipo subyacente. Si HasValue es true, Value contiene un valor significativo. Si HasValue es false, al tener acceso a Value se producirá una excepción InvalidOperationException.

En este ejemplo, el miembro HasValue se utiliza para comprobar si la variable contiene un valor antes de mostrarlo.

int? x = 10;
if (x.HasValue)
{
    System.Console.WriteLine(x.Value);
}
else
{
    System.Console.WriteLine("Undefined");
}

La comprobación de un valor también puede realizarse tal y como se muestra en el siguiente ejemplo:

int? y = 10;
if (y != null)
{
    System.Console.WriteLine(y.Value);
}
else
{
    System.Console.WriteLine("Undefined");
}

Conversiones explícitas

Un tipo que acepta valores NULL se puede convertir en un tipo normal mediante el uso explícito de la conversión o a través de la propiedad Value. Por ejemplo:

int? n = null;

//int m1 = n;      // Will not compile.
int m2 = (int)n;   // Compiles, but will create an exception if n is null.
int m3 = n.Value;  // Compiles, but will create an exception if n is null.

Si se define una conversión definida por el usuario entre dos tipos de datos, la misma conversión se puede utilizar con las versiones que aceptan valores NULL de estos tipos de datos.

Conversiones implícitas

Una variable de tipo que acepta valores NULL puede establecerse como null con la palabra clave null, tal y como se muestra en el siguiente ejemplo:

int? n1 = null;

La conversión de un tipo ordinario a un tipo que acepta valores NULL, es implícita.

int? n2;
n2 = 10;  // Implicit conversion.

Operadores

Los operadores predefinidos unarios y binarios así como cualquier operador definido por el usuario que exista para los tipos de valor también pueden ser utilizados por los tipos que aceptan valores NULL. Estos operadores generan un valor null si los operandos son null; de lo contrario, el operador utiliza el valor contenido para calcular el resultado. Por ejemplo:

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

a++;         // Increment by 1, now a is 11.
a = a * 10;  // Multiply by 10, now a is 110.
a = a + b;   // Add b, now a is null.

Al realizar comparaciones con tipos que aceptan valores NULL, si el valor de uno de estos tipos es null y el del otro tipo no lo es, todas las comparaciones se evalúan como false, salvo para != (desigualdad). Es importante no dar por supuesto que, porque una comparación concreta devuelva false, el caso contrario devolverá true. En el ejemplo siguiente, 10 no es mayor, menor ni igual que null. Solo num1 != num2 se evalúa como true.

int? num1 = 10;
int? num2 = null;
if (num1 >= num2)
{
    Console.WriteLine("num1 is greater than or equal to num2");
}
else
{
    // This clause is selected, but num1 is not less than num2.
    Console.WriteLine("num1 >= num2 returned false (but num1 < num2 also is false)");
}

if (num1 < num2)
{
    Console.WriteLine("num1 is less than num2");
}
else
{
    // The else clause is selected again, but num1 is not greater than
    // or equal to num2.
    Console.WriteLine("num1 < num2 returned false (but num1 >= num2 also is false)");
}

if (num1 != num2)
{
    // This comparison is true, num1 and num2 are not equal.
    Console.WriteLine("Finally, num1 != num2 returns true!");
}

// Change the value of num1, so that both num1 and num2 are null.
num1 = null;
if (num1 == num2)
{
    // The equality comparison returns true when both operands are null.
    Console.WriteLine("num1 == num2 returns true when the value of each is null");
}

/* Output:
 * num1 >= num2 returned false (but num1 < num2 also is false)
 * num1 < num2 returned false (but num1 >= num2 also is false)
 * Finally, num1 != num2 returns true!
 * num1 == num2 returns true when the value of each is null
 */

Una comparación de igualdad de dos tipos que aceptan valores NULL y que son null se evalúa como true.

El Operador

El operador ?? define un valor predeterminado que se devuelve cuando un tipo que acepta valores NULL se asigna a un tipo que no acepta valores NULL.

int? c = null;

// d = c, unless c is null, in which case d = -1.
int d = c ?? -1;

Este operador también se puede utilizar con muchos tipos que no aceptan valores NULL. Por ejemplo:

int? e = null;
int? f = null;

// g = e or f, unless e and f are both null, in which case g = -1.
int g = e ?? f ?? -1;

El tipotype

El tipo bool? que acepta valores NULL, puede contener tres valores diferentes: true, false y null. Para obtener información sobre cómo convertir un tipo bool? en un tipo booleano, vea Cómo: Convertir con seguridad de bool? en bool (Guía de programación de C#).

Los tipos booleanos que aceptan valores NULL son como el tipo de variable booleano que se utiliza en SQL. Para asegurarse de que los resultados generados por los operadores & y | sean coherentes con el tipo booleano de tres valores de SQL, se proporcionan los siguientes operadores predefinidos:

bool? operator &(bool? x, bool? y)

bool? operator |(bool? x, bool? y)

Los resultados de estos operadores se muestran en la siguiente tabla:

X

y

x&y

x|y

true

true

true

true

true

false

false

true

true

null

null

true

false

true

false

true

false

false

false

false

false

null

false

null

null

true

null

true

null

false

false

null

null

null

null

null

Vea también

Referencia

Tipos que aceptan valores NULL (Guía de programación de C#)

Aplicar la conversión boxing a tipos que aceptan valores NULL (Guía de programación de C#)

Conceptos

Guía de programación de C#