C# Language Propagación nula


Ejemplo

El ?. operador y ?[...] operador se llaman el operador condicional nulo . A veces también se hace referencia a otros nombres, como el operador de navegación segura .

Esto es útil, porque si el . El operador (miembro de acceso) se aplica a una expresión que se evalúa como null , el programa lanzará una NullReferenceException . Si el desarrollador usa en su lugar el ?. Operador (condicional nulo), la expresión se evaluará como nula en lugar de lanzar una excepción.

Tenga en cuenta que si el ?. se usa el operador y la expresión es no nula, ?. y . son equivalentes


Lo esencial

var teacherName = classroom.GetTeacher().Name;
// throws NullReferenceException if GetTeacher() returns null

Ver demostración

Si el classroom no tiene un profesor, GetTeacher() puede devolver null . Cuando es null y se accede a la propiedad Name , se lanzará una NullReferenceException .

Si modificamos esta declaración para utilizar el ?. Sintaxis, el resultado de toda la expresión será null :

var teacherName = classroom.GetTeacher()?.Name;
// teacherName is null if GetTeacher() returns null

Ver demostración

Posteriormente, si el classroom también pudiera ser null , también podríamos escribir esta declaración como:

var teacherName = classroom?.GetTeacher()?.Name;
// teacherName is null if GetTeacher() returns null OR classroom is null

Ver demostración

Este es un ejemplo de cortocircuito: cuando cualquier operación de acceso condicional que utiliza el operador condicional nulo se evalúa como nulo, la expresión completa se evalúa como nula inmediatamente, sin procesar el resto de la cadena.

Cuando el miembro terminal de una expresión que contiene el operador condicional nulo es de un tipo de valor, la expresión se evalúa como un Nullable<T> de ese tipo y, por lo tanto, no puede usarse como un reemplazo directo de la expresión sin ?. .

bool hasCertification = classroom.GetTeacher().HasCertification;
// compiles without error but may throw a NullReferenceException at runtime

bool hasCertification = classroom?.GetTeacher()?.HasCertification;
// compile time error: implicit conversion from bool? to bool not allowed

bool? hasCertification = classroom?.GetTeacher()?.HasCertification;
// works just fine, hasCertification will be null if any part of the chain is null

bool hasCertification = classroom?.GetTeacher()?.HasCertification.GetValueOrDefault();
// must extract value from nullable to assign to a value type variable

Usar con el operador de unión nula (??)

Puede combinar el operador de condición nula con el operador de unión nula ( ?? ) para devolver un valor predeterminado si la expresión se resuelve en null . Usando nuestro ejemplo anterior:

var teacherName = classroom?.GetTeacher()?.Name ?? "No Name";
// teacherName will be "No Name" when GetTeacher() 
// returns null OR classroom is null OR Name is null

Usar con indexadores

El operador condicional nulo se puede utilizar con los indizadores :

var firstStudentName = classroom?.Students?[0]?.Name;

En el ejemplo anterior:

  • ¿El primero ?. Asegura que el classroom no sea null .
  • El segundo ? asegura que toda la colección Students no sea null .
  • ¿El tercero ?. después de que el indexador se asegure de que el indexador [0] no devolvió un objeto null . Se debe tener en cuenta que esta operación aún puede lanzar una IndexOutOfRangeException .

Usar con funciones vacias

El operador de condición nula también se puede utilizar con funciones de void . Sin embargo, en este caso, la declaración no se evaluará como null . Simplemente evitará una NullReferenceException .

List<string> list = null;
list?.Add("hi");          // Does not evaluate to null

Utilizar con la invocación de eventos

Asumiendo la siguiente definición de evento:

private event EventArgs OnCompleted;

Cuando se invoca un evento, tradicionalmente, es una buena práctica verificar si el evento es null en caso de que no haya suscriptores presentes:

var handler = OnCompleted;
if (handler != null)
{
    handler(EventArgs.Empty);
}

Dado que se ha introducido el operador condicional nulo, la invocación se puede reducir a una sola línea:

OnCompleted?.Invoke(EventArgs.Empty);

Limitaciones

El operador condicional nulo produce rvalue, no lvalue, es decir, no se puede usar para la asignación de propiedades, suscripción de eventos, etc. Por ejemplo, el siguiente código no funcionará:

// Error: The left-hand side of an assignment must be a variable, property or indexer
Process.GetProcessById(1337)?.EnableRaisingEvents = true;
// Error: The event can only appear on the left hand side of += or -=
Process.GetProcessById(1337)?.Exited += OnProcessExited;

Gotchas

Tenga en cuenta que:

int? nameLength = person?.Name.Length;    // safe if 'person' is null

no es lo mismo que

int? nameLength = (person?.Name).Length;  // avoid this

porque lo primero corresponde a:

int? nameLength = person != null ? (int?)person.Name.Length : null;

y este último corresponde a:

int? nameLength = (person != null ? person.Name : null).Length;

A pesar del operador ternario ?: Se utiliza aquí para explicar la diferencia entre dos casos, estos operadores no son equivalentes. Esto se puede demostrar fácilmente con el siguiente ejemplo:

void Main()
{
    var foo = new Foo();
    Console.WriteLine("Null propagation");
    Console.WriteLine(foo.Bar?.Length);

    Console.WriteLine("Ternary");
    Console.WriteLine(foo.Bar != null ? foo.Bar.Length : (int?)null);
}

class Foo
{
    public string Bar
    {
        get
        {
            Console.WriteLine("I was read");
            return string.Empty;
        }
    }
}

Qué salidas:

Propagación nula
Fui leído
0
Ternario
Fui leído
Fui leído
0

Ver demostración

Para evitar múltiples invocaciones equivalentes sería:

var interimResult = foo.Bar;
Console.WriteLine(interimResult != null ? interimResult.Length : (int?)null);

Y esta diferencia explica en parte por qué el operador de propagación nula aún no se admite en los árboles de expresiones.