C# Language Propagazione nulla


Esempio

Il ?. operatore e operatore ?[...] sono chiamati operatori condizionali nulli . A volte viene anche indicato con altri nomi come l' operatore di navigazione sicura .

Questo è utile, perché se il . L'operatore (accessor membro) viene applicato a un'espressione che restituisce null , il programma genererà NullReferenceException . Se lo sviluppatore utilizza invece il ?. (null-condizionale), l'espressione valuterà a null invece di generare un'eccezione.

Si noti che se il ?. operatore viene utilizzato e l'espressione non è nullo, ?. e . sono equivalenti.


Nozioni di base

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

Visualizza la demo

Se l' classroom non ha un insegnante, GetTeacher() può restituire null . Quando è null e si accede alla proprietà Name , verrà generata NullReferenceException .

Se modifichiamo questa affermazione per usare il ?. sintassi, il risultato dell'intera espressione sarà null :

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

Visualizza la demo

Successivamente, se la classroom potrebbe anche essere null , potremmo scrivere anche questa affermazione come:

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

Visualizza la demo

Questo è un esempio di cortocircuito: quando qualsiasi operazione di accesso condizionale che utilizza l'operatore null-condizionale restituisce null, l'intera espressione restituisce immediatamente null, senza elaborare il resto della catena.

Quando il membro terminale di un'espressione che contiene l'operatore condizionale nullo è di un tipo valore, l'espressione Nullable<T> un Nullable<T> di quel tipo e quindi non può essere utilizzato come sostituzione diretta dell'espressione senza ?. .

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

Utilizzare con l'operatore Null-Coalescing (??)

È possibile combinare l'operatore null-condizionale con l'operatore Null-coalescing ( ?? ) per restituire un valore predefinito se l'espressione si risolve in null . Usando il nostro esempio sopra:

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

Utilizzare con gli indicizzatori

L'operatore null-condizionale può essere utilizzato con gli indicizzatori :

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

Nell'esempio sopra:

  • Il primo ?. assicura che l' classroom non sia null .
  • Il secondo ? assicura che l'intera raccolta Students non sia null .
  • Il terzo ?. dopo che l'indicizzatore assicura che l'indicizzatore [0] non ha restituito un oggetto null . Va notato che questa operazione può ancora lanciare un IndexOutOfRangeException .

Utilizzare con funzioni void

L'operatore Null-condition può anche essere utilizzato con le funzioni void . Tuttavia, in questo caso, la dichiarazione non valuterà null . NullReferenceException solo una NullReferenceException .

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

Utilizzare con invocazione di eventi

Supponendo la seguente definizione di evento:

private event EventArgs OnCompleted;

Quando si invoca un evento, tradizionalmente, è consigliabile verificare se l'evento è null nel caso in cui non siano presenti abbonati:

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

Poiché è stato introdotto l'operatore null-condizionale, l'invocazione può essere ridotta a una singola riga:

OnCompleted?.Invoke(EventArgs.Empty);

limitazioni

L'operatore Null-condition produce rvalue, non lvalue, cioè non può essere utilizzato per l'assegnazione di proprietà, la sottoscrizione di eventi ecc. Ad esempio, il seguente codice non funzionerà:

// 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

Nota che:

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

non è la stessa di:

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

perché il primo corrisponde a:

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

e quest'ultimo corrisponde a:

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

Nonostante l'operatore ternario ?: Qui viene utilizzato per spiegare la differenza tra due casi, questi operatori non sono equivalenti. Questo può essere facilmente dimostrato con il seguente esempio:

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

Quali uscite:

Propagazione nulla
Sono stato letto
0
Ternario
Sono stato letto
Sono stato letto
0

Visualizza la demo

Per evitare invocazioni multiple equivalenti sarebbe:

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

E questa differenza spiega in qualche modo perché l'operatore di propagazione null non è ancora supportato negli alberi di espressione.