Exprimer votre intention
- 5 minutes
Au cours de la leçon précédente, vous avez appris comment le compilateur C# pouvait effectuer une analyse statique pour vous aider à vous protéger de l’exception NullReferenceException. Vous avez également appris à activer un contexte pouvant accepter la valeur Null. Dans cette unité, vous en apprendrez davantage sur l’expression explicite de votre intention dans un contexte nullable.
Déclaration de variables
Avec un contexte compatible avec la valeur Null, vous bénéficiez d’une plus grande visibilité sur la façon dont le compilateur voit votre code. Vous pouvez agir sur les avertissements générés dans un contexte acceptant les valeurs Null et par là-même définir explicitement vos intentions. Par exemple, poursuivons l’examen du code FooBar et examinons la déclaration et l’affectation :
// Define as nullable
FooBar? fooBar = null;
Notez le ? ajouté à FooBar. Cela indique au compilateur que vous souhaitez explicitement autoriser fooBar à avoir une valeur Null. Si vous n’avez pas l’intention d’utiliser fooBar avec une valeur Null, mais que vous souhaitez tout de même éviter l’avertissement, tenez compte des points suivants :
// Define as non-nullable, but tell compiler to ignore warning
// Same as FooBar fooBar = default!;
FooBar fooBar = null!;
Cet exemple ajoute l’opérateur null-indulgent (!) à null, qui indique au compilateur que vous initialisez explicitement cette variable en tant que valeur Null. Le compilateur n’émettra pas d’avertissements sur la valeur Null pour cette référence.
Une bonne pratique consiste à affecter à vos variables non-nulles des valeurs autres que null lorsqu’elles sont déclarées, si possible.
// Define as non-nullable, assign using 'new' keyword
FooBar fooBar = new(Id: 1, Name: "Foo");
Opérateurs
Comme indiqué dans la leçon précédente, C# définit plusieurs opérateurs pour exprimer votre intention autour des types référence pouvant accepter la valeur Null.
Opérateur null-indulgent (!)
L’opérateur null-indulgent (!) a été mentionné dans la section précédente. Il indique au compilateur d’ignorer l’avertissement CS8600. Il s’agit d’un moyen d’indiquer au compilateur que vous savez ce que vous faites, mais il faut que vous sachiez réellement ce que vous faites !
Quand vous initialisez des types non-nullable alors qu’un contexte pouvant accepter la valeur Null est activé, vous pouvez avoir besoin de demander explicitement au compilateur d’utiliser l’indulgence. Considérons par exemple le code suivant :
#nullable enable
using System.Collections.Generic;
var fooList = new List<FooBar>
{
new(Id: 1, Name: "Foo"),
new(Id: 2, Name: "Bar")
};
FooBar fooBar = fooList.Find(f => f.Name == "Bar");
// The FooBar type definition for example.
record FooBar(int Id, string Name);
Dans l’exemple précédent, FooBar fooBar = fooList.Find(f => f.Name == "Bar"); génère un avertissement CS8600, car Find peut retourner null. Ce null potentiel peut être attribué à fooBar,qui a une valeur non-nullable dans ce contexte. Toutefois, dans cet exemple fictif, nous savons que Find ne retournera jamais null comme écrit. Vous pouvez exprimer cette intention au compilateur en utilisant l’opérateur null-indulgent :
FooBar fooBar = fooList.Find(f => f.Name == "Bar")!;
Notez le ! à la fin de fooList.Find(f => f.Name == "Bar"). Cela indique au compilateur que vous savez que l’objet retourné par la méthode Find peut être null, et que c’est correct.
Vous pouvez appliquer l’opérateur null-forgiving sur un objet inlined avant un appel de méthode ou avant une évaluation de propriété. Prenons un autre exemple fictif :
List<FooBar>? fooList = FooListFactory.GetFooList();
// Declare variable and assign it as null.
FooBar fooBar = fooList.Find(f => f.Name == "Bar")!; // generates warning
static class FooListFactory
{
public static List<FooBar>? GetFooList() =>
new List<FooBar>
{
new(Id: 1, Name: "Foo"),
new(Id: 2, Name: "Bar")
};
}
// The FooBar type definition for example.
record FooBar(int Id, string Name);
Dans l’exemple précédent :
GetFooListest une méthode statique qui retourne un type Nullable,List<FooBar>?.fooListse voit attribuer la valeur retournée parGetFooList.- Le compilateur génère un avertissement sur
fooList.Find(f => f.Name == "Bar");, car la valeur attribuée àfooListpeut êtrenull. - Si
fooListn’est pasnull,Findpeut retournernull, mais nous savons que ce ne sera pas le cas, ainsi l’opérateur null-indulgent est appliqué.
Vous pouvez appliquer l’opérateur null-indulgent à fooList pour désactiver l’avertissement :
FooBar fooBar = fooList!.Find(f => f.Name == "Bar")!;
Note
Vous devez utiliser l’opérateur null-forgiving judicieusement. L’utiliser simplement pour rejeter un avertissement signifie que vous indiquez au compilateur de ne pas vous aider à découvrir les éventuelles erreurs liées aux valeurs nulles. Utilisez-le avec modération et uniquement lorsque vous êtes sûr de vous.
Pour plus d’informations, consultez Opérateur ! (null-forgiving) (Informations de référence sur C#).
Opérateur de coalescence nulle (??)
Lorsque vous travaillez avec des types annulables, vous pourriez avoir besoin d’évaluer s’ils sont actuellement null et prendre certaines mesures en conséquence. Par exemple, lorsqu’un type Nullable s’est vu affecté null ou qu’il n’a pas été initialisé, vous devrez peut-être lui attribuer une valeur non-null. C’est là que l’opérateur de coalescence nulle (??) est utile.
Prenons l’exemple suivant :
public void CalculateSalesTax(IStateSalesTax? salesTax = null)
{
salesTax ??= DefaultStateSalesTax.Value;
// Safely use salesTax object.
}
Dans le code C# précédent :
- Le paramètre
salesTaxest défini comme un typeIStateSalesTaxnullable. - Dans le corps de méthode, le
salesTaxest attribué de manière conditionnelle à l’aide de l’opérateur de coalescence nulle.- Cela garantit que si
salesTaxa été transmis en tant quenull, il aura une valeur.
- Cela garantit que si
Conseil
Cela fonctionne de la même manière que le code C# suivant :
public void CalculateSalesTax(IStateSalesTax? salesTax = null)
{
if (salesTax is null)
{
salesTax = DefaultStateSalesTax.Value;
}
// Safely use salesTax object.
}
Voici l’exemple d’un autre idiome C# courant où l’opérateur de coalescence nulle peut être utile :
public sealed class Wrapper<T> where T : new()
{
private T _source;
// If given a source, wrap it. Otherwise, wrap a new source:
public Wrapper(T source = null) => _source = source ?? new T();
}
Le code C# précédent :
- Définit une classe wrapper générique, où le paramètre de type générique est restreint à
new(). - Le constructeur accepte un paramètre
T sourcedont la valeur par défaut estnull. - Le
_sourceenveloppé est initialisé de manière conditionnelle à unnew T().
Pour plus d’informations, consultez Opérateurs ?? et ??= (Informations de référence sur C#).
Opérateur conditionnel Null (?.)
Lorsque vous travaillez avec des types Nullable, vous êtes parfois appelé à effectuer des actions conditionnelles en fonction de l’état d’un objet null. Par exemple, dans l'unité précédente, l'enregistrement FooBar a été utilisé pour démontrer NullReferenceException par le déréférencement de null. Cela était dû à l’appel de ToString. Gardons le même exemple, mais maintenant en appliquant l’opérateur conditionnel Null :
using System;
// Declare variable and assign it as null.
FooBar fooBar = null;
// Conditionally dereference variable.
var str = fooBar?.ToString();
Console.Write(str);
// The FooBar type definition.
record FooBar(int Id, string Name);
Le code C# précédent :
- Déréférencez
fooBarde manière conditionnelle, en assignant le résultat deToStringà la variablestr.- La variable
strest de typestring?(chaîne pouvant accepter la valeur Null).
- La variable
- Il écrit la valeur de
strdans une sortie standard, qui n’est rien. - L’appel de
Console.Write(null)est valide, donc il n’y a pas d’avertissement. - Vous auriez reçu un avertissement si vous aviez appelé
Console.Write(str.Length), car vous auriez risqué de déréférencer la valeur Null.
Conseil
Cela fonctionne de la même manière que le code C# suivant :
using System;
// Declare variable and assign it as null.
FooBar fooBar = null;
// Conditionally dereference variable.
string str = (fooBar is not null) ? fooBar.ToString() : default;
Console.Write(str);
// The FooBar type definition.
record FooBar(int Id, string Name);
Vous pouvez combiner les opérateurs pour affiner davantage votre intention. Par exemple, vous pouvez rattacher les opérateurs ?. et ?? :
FooBar fooBar = null;
var str = fooBar?.ToString() ?? "unknown";
Console.Write(str); // output: unknown
Pour plus d’informations, consultez Opérateurs ?. et ?[] (null-conditional).
Résumé
Dans cette unité, vous avez appris à exprimer vos intentions de nullité dans le code. Dans la prochaine leçon, vous allez appliquer ce que vous avez appris à un projet existant.