C# Tipos anulables (Nullable)

En C#,int、float、bool、DateTimeetc. sontipos de valor, por defecto deben tener un valor y no pueden sernull:

int a = null;  // 编译错误:无法将 null 转换为 int

Los tipos anulables (Nullable Types)resuelven este problema: permiten que los tipos de valor tengan un estado adicional de "sin valor" (es decir,null), lo que es muy útil en escenarios donde los datos pueden faltar, como campos de base de datos, valores de retorno de API, etc.

La forma de declararlo es muy sencilla: agrega un?:

int? a = null;  // 合法
int? b = 42;    // 合法

El de aquíint?SíNullable<int>es azúcar sintáctico (forma abreviada), ambos son completamente equivalentes:

Nullable&lt;int&gt; a = null;   // 完整写法
int? a = null;              // 简写,效果相同

La siguiente figura muestra cómo funcionan los tipos anulables en memoria: utiliza un marcador booleano adicional para registrar "si tiene valor":

Estructura en memoria de Nullable<T> int (tipo de valor normal) Value: 42 4 bytes, siempre tiene valor Envoltura Nullable<int> (tipo anulable) HasValue Value 1 byte 4 bytes Total: 5+ bytes int? x = 42; true ✓ 42 HasValue = true,Value = 42 int? y = null; false ✗ Ninguno HasValue = false, acceder a Value lanza una excepción

Diferencia entre el signo de interrogación simple ? y el doble ??

En C#, los dos operadores relacionados con los tipos anulables se usan a menudo juntos, pero sus significados son completamente diferentes:

Operador Nombre Uso Ejemplo
? Modificador de tipo anulable Permite que un tipo de valor pueda sernull int? i = 3;Equivalente aNullable<int> i = new Nullable<int>(3);
?? Operador de fusión nula (Null-Coalescing Operator) Cuando la variable esnullproporciona un valor predeterminado int result = i ?? 0;

Una comparación sencilla:

int i;     // 普通值类型,默认值为 0,永远不能是 null
int? ii;   // 可空类型,默认值为 null

Declaración y asignación de tipos anulables

Sintaxis de declaración

<data_type>? <variable_name> = null;

Por ejemplo:

int? age = null;
double? temperature = 36.6;
bool? isActive = new bool?();   // 显式构造,默认为 null
DateTime? birthday = null;

Nullable<T>Puede representar el rango normal de su tipo de valor subyacente, más unnullvalor. Por ejemploNullable<int>puede ser-2,147,483,648llegar2,147,483,647cualquier entero entre ..., onull。

Ejemplo completo

Ejemplo

using System;

namespace NullableDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            // Declarar variables anulables de diferentes tipos
            int? num1 = null;
            int? num2 = 45;
            double? num3 = new double?();   // null
            double? num4 = 3.14157;
            bool? boolVal = new bool?();    // null

            // Mostrar el valor (null se mostrará como cadena vacía)
            Console.WriteLine($"num1 = {num1 ?? 0}");         // 0
            Console.WriteLine($"num2 = {num2 ?? 0}");         // 45
            Console.WriteLine($"num3 = {num3 ?? 0.0}");       // 0
            Console.WriteLine($"num4 = {num4 ?? 0.0}");       // 3.14157
            Console.WriteLine($"boolVal = {boolVal}");         // (vacío)
        }
    }
}

Resultado de salida:

num1 = 0
num2 = 45
num3 = 0
num4 = 3.14157
boolVal =

Operador de fusión nula (??)

??El operador se usa para proporcionar a los tipos anulables o tipos de referencia unvalor predeterminado de respaldo. Cuando el lado izquierdo no esnulldevuelve el valor del lado izquierdo; de lo contrario, devuelve el valor del lado derecho:

<表达式1> ?? <表达式2>
  • Si<表达式1>no esnull, devuelve<表达式1>;
  • de lo contrario, devuelve<表达式2>。

Desde C# 8.0, también se puede usar??=(operador de asignación de fusión nula), solo cuando la variable esnullse realiza la asignación:

int? x = null;
x ??= 10;   // x 为 null,赋值为 10
x ??= 20;   // x 已有值 10,不再赋值

Ejemplo

using System;

namespace NullableDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            double? num1 = null;
            double? num2 = 3.14157;

            // ?? proporciona el valor predeterminado
            double result1 = num1 ?? 5.34;   // num1 es null → devuelve 5.34
            double result2 = num2 ?? 5.34;   // num2 tiene valor → devuelve 3.14157

            Console.WriteLine($"num1 ?? 5.34 = {result1}");   // 5.34
            Console.WriteLine($"num2 ?? 5.34 = {result2}");   // 3.14157

            // Uso en cadena: proporcionar múltiples valores alternativos
            int? a = null;
            int? b = null;
            int? c = 42;
            int value = a ?? b ?? c ?? 0;    // Se intenta uno por uno, finalmente se obtiene 42
            Console.WriteLine($"a ?? b ?? c ?? 0 = {value}"); // 42
        }
    }
}

Resultado de salida:

num1 ?? 5.34 = 5.34
num2 ?? 5.34 = 3.14157
a ?? b ?? c ?? 0 = 42

Propiedades y métodos comunes de los tipos anulables

Miembro Descripción Ejemplo
.HasValue Determina si la variable tiene valor (devuelvebool) if (num.HasValue) { ... }
.Value Obtiene el valor real (si esnulllanzaInvalidOperationException) int x = num.Value;
.GetValueOrDefault() Obtiene el valor de forma segura, si esnulldevuelve el valor predeterminado del tipo (como0) num.GetValueOrDefault()
.GetValueOrDefault(T) Obtiene el valor de forma segura, si esnulldevuelve el valor predeterminado especificado num.GetValueOrDefault(100)
?? Operador de fusión nula (azúcar sintáctico, equivalente aGetValueOrDefault) int result = num ?? 100;

Ejemplo

using System;

namespace NullableDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            int? num = null;

            // HasValue + Value (método tradicional)
            if (num.HasValue)
                Console.WriteLine($"Valor: {num.Value}");
            else
                Console.WriteLine("num no tiene valor");

            // Obtener el valor predeterminado de forma segura
            Console.WriteLine(num.GetValueOrDefault());      // 0
            Console.WriteLine(num.GetValueOrDefault(99));    // 99
            Console.WriteLine(num ?? 99);                    // 99 (forma equivalente)

            // ⚠ Nota: acceder directamente a .Value lanzará una excepción
            // int x = num.Value;  // InvalidOperationException!
        }
    }
}

Escenarios de aplicación práctica

Los tipos anulables son muy comunes en el desarrollo real, especialmente al manejar datos que pueden faltar:

Mapeo de campos de base de datos

Los campos de la base de datos pueden serNULL, con los tipos anulables se puede hacer una correspondencia precisa:

ID de usuario Edad Activo
1 28 true
2 null false
int? age = GetUserAgeFromDB(userId: 2);

// 安全处理 null
string display = age.HasValue
    ? $"用户年龄:{age.Value}"
    : "年龄未知";

// 或者更简洁的写法
string display2 = $"用户年龄:{age ?? 0}";

Parámetros opcionales y configuración

// 配置项可能未设置
int? timeout = GetConfigValue("timeout");
int actualTimeout = timeout ?? 30;  // 未设置时使用默认值 30 秒

Comprobación de nulos en cadena (C# 6.0+)

// 使用 ?. 运算符安全访问可能为 null 的对象
string? name = user?.Address?.City?.Name;
// 如果 user、Address、City 中任何一个为 null,结果就是 null,不会抛异常

Resumen

Función Ejemplo Descripción
Definir tipo anulable int? x = null; Equivalente aNullable<int>
Determinar si tiene valor x.HasValue Devuelvetrueofalse
Obtener el valor (no seguro) x.Value Si esnulllanzaInvalidOperationException
Obtener el valor predeterminado x ?? 0 Si esnulldevuelve el valor del lado derecho
Obtener el valor predeterminado especificado x.GetValueOrDefault(10) Si esnulldevuelve 10
Asignación de fusión nula x ??= 10 Solo cuandoxParanullse asigna el valor (C# 8.0+)
Acceso de navegación segura user?.Name siuserParanullentonces devuelvenull(C# 6.0+)

Los "tipos de referencia anulables" de C# 8.0

Desde C# 8.0, se introdujeron lostipos de referencia anulables (Nullable Reference Types), que junto con lostipos de valor anulablesson dos mecanismos diferentes:

Elemento de comparación Tipos de valor anulables Tipos de referencia anulables
Ejemplo int? string?
Aplica a Tipos de valor (int、bool、structetc.) Tipos de referencia (string、class, arreglos, etc.)
Implementación En tiempo de ejecución, medianteNullable<T>implementación con struct El compilador realiza análisis estático, no cambia el comportamiento en tiempo de ejecución.
Estado predeterminado Siempre presente (desde C# 1.0) Debe habilitarse en el proyecto<Nullable>enable</Nullable>
comprobación de null usar.HasValueDeterminar El compilador emite advertencias (no errores)

Los tipos de referencia anulables son una herramienta de verificación de seguridad en tiempo de compilación: no cambian el comportamiento en tiempo de ejecución del programa, pero en los lugares donde el código podría producirNullReferenceExceptionemitirá advertencias del compilador, ayudándote a detectar problemas potenciales en la etapa de desarrollo.

Otras extensiones