Exprimer votre intention
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 leçon, vous en apprendrez davantage sur l’expression explicite de votre intention dans un contexte pouvant accepter la valeur Null.
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 à attribuer à vos variables non-nullable les valeurs 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 :
GetFooList
est une méthode statique qui retourne un type Nullable,List<FooBar>?
.fooList
se 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 àfooList
peut êtrenull
. - Si
fooList
n’est pasnull
,Find
peut 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")!;
Notes
Vous devez utiliser l’opérateur null-forgiving judicieusement. L’utiliser simplement pour ignorer un avertissement signifie que vous indiquez au compilateur de ne pas vous aider à découvrir les éventuels incidents sur les valeurs Null. 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 utilisez des types Nullable, vous serez peut-être amené à évaluer s’il s’agit actuellement de null
et s’ils effectuent certaines actions. 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
salesTax
est défini comme pouvant accepter la valeur NullIStateSalesTax
. - Dans le corps de méthode, le
salesTax
est attribué de manière conditionnelle à l’aide de l’opérateur de coalescence nulle.- Cela garantit que si
salesTax
a é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 source
dont la valeur par défaut estnull
. - Le
_source
inclus dans un wrapper est initialisé de manière conditionnelle dans 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
avait été utilisé pour illustrer l’exception 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
fooBar
de manière conditionnelle, en assignant le résultat deToString
à la variablestr
.- La variable
str
est de typestring?
(chaîne pouvant accepter la valeur Null).
- La variable
- Il écrit la valeur de
str
dans 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 leçon, vous avez appris à exprimer votre intention de possibilité de valeur Null dans le code. Dans la prochaine leçon, vous allez appliquer ce que vous avez appris à un projet existant.