C# Language Propagation nulle


Exemple

Le ?. L'opérateur et l'opérateur ?[...] sont appelés l' opérateur null-conditionnel . Il est également parfois appelé par d'autres noms tels que l' opérateur de navigation sécurisé .

Ceci est utile, car si le . L'opérateur (accesseur membre) est appliqué à une expression dont la valeur est null , le programme lancera une NullReferenceException . Si le développeur utilise plutôt le ?. (null-conditionnel) opérateur, l'expression évaluera à null au lieu de lancer une exception.

Notez que si le ?. l'opérateur est utilisé et l'expression est non nulle, ?. et . sont équivalents.


Les bases

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

Voir la démo

Si la classroom n'a pas d'enseignant, GetTeacher() peut renvoyer null . Lorsqu'il est null et que la propriété Name est NullReferenceException , une NullReferenceException sera lancée.

Si nous modifions cette déclaration pour utiliser le ?. la syntaxe, le résultat de l’expression entière sera null :

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

Voir la démo

Par la suite, si la classroom pouvait également être null , nous pourrions également écrire cette déclaration comme suit:

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

Voir la démo

Voici un exemple de mise en court-circuit: Lorsqu'une opération d'accès conditionnel utilisant l'opérateur null-conditionnel a la valeur null, l'expression entière est évaluée immédiatement à null, sans traitement du reste de la chaîne.

Lorsque le membre terminal d'une expression contenant l'opérateur null-conditionnel est d'un type valeur, l'expression est évaluée à un Nullable<T> de ce type et ne peut donc pas être utilisée comme remplacement direct de l'expression sans ?. .

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

Utiliser avec l'opérateur Null-Coalescing (??)

Vous pouvez combiner l'opérateur null-conditionnel avec l'opérateur Null-coalescing ( ?? ) pour renvoyer une valeur par défaut si l'expression est résolue à null . En utilisant notre exemple ci-dessus:

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

Utiliser avec des indexeurs

L'opérateur null-conditionnel peut être utilisé avec les indexeurs :

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

Dans l'exemple ci-dessus:

  • Le premier ?. s'assure que la classroom n'est pas null .
  • La seconde ? s'assure que toute la collection Students n'est pas null .
  • Le troisième ?. après l'indexeur s'assure que l'indexeur [0] n'a pas renvoyé d'objet null . Il convient de noter que cette opération peut toujours lancer une IndexOutOfRangeException .

Utiliser avec fonctions vides

L'opérateur Null-Conditionnel peut également être utilisé avec les fonctions void . Cependant, dans ce cas, l'instruction ne sera pas évaluée à null . Cela empêchera simplement une NullReferenceException .

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

Utiliser avec l'invocation d'événement

En supposant la définition d'événement suivante:

private event EventArgs OnCompleted;

Lors de l'appel d'un événement, traditionnellement, il est recommandé de vérifier si l'événement est null si aucun abonné n'est présent:

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

Comme l'opérateur null-conditionnel a été introduit, l'invocation peut être réduite à une seule ligne:

OnCompleted?.Invoke(EventArgs.Empty);

Limites

L'opérateur Null-conditionnel produit rvalue, pas lvalue, c'est-à-dire qu'il ne peut pas être utilisé pour l'affectation de propriété, l'abonnement à un événement, etc. Par exemple, le code suivant ne fonctionnera pas:

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

Notez que:

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

n'est pas la même chose que:

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

parce que le premier correspond à:

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

et ce dernier correspond à:

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

Malgré l'opérateur ternaire ?: Est utilisé ici pour expliquer la différence entre deux cas, ces opérateurs ne sont pas équivalents. Cela peut être facilement démontré avec l'exemple suivant:

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

Quelles sorties:

Propagation nulle
J'ai été lu
0
Ternaire
J'ai été lu
J'ai été lu
0

Voir la démo

Pour éviter les invocations multiples, l'équivalent serait:

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

Et cette différence explique en partie pourquoi l'opérateur de propagation nul n'est pas encore pris en charge dans les arbres d'expression.