Types références Nullables
Dans un contexte nullable-oblivious (oubliant la valeur Null), tous les types de référence étaient nullable (pouvant accepter la valeur Null). Les types de référence pouvant accepter la valeur Null font référence à un groupe de fonctionnalités activées dans un contexte pouvant accepter la valeur Null qui réduisent la probabilité que votre code provoque la levée System.NullReferenceExceptiondu runtime . Les types de référence pouvant accepter la valeur Null incluent trois fonctionnalités qui vous permettent d’éviter ces exceptions, notamment la possibilité de marquer explicitement un type de référence comme pouvant accepter la valeur Null :
- Analyse de flux statique améliorée qui détermine si une variable peut être
null
avant de la déréférencer. - Attributs qui annotent les API afin que l’analyse de flux détermine l’état Null.
- Annotations des variables que les développeurs utilisent pour déclarer explicitement l’état Null prévu pour une variable.
Le compilateur effectue le suivi du null-state (état null) de chaque expression dans votre code au moment de la compilation. Le null-state a l’une des trois valeurs suivantes :
- not-null: l’expression est connue comme n’étant pas
null
. - maybe-null: l’expression peut être
null
. - oblivious : le compilateur ne peut pas déterminer le null-state de l’expression.
Les annotations de variable déterminent la nullabilité d’une variable de type référence :
- non-nullable : si vous attribuez une valeur
null
ou une expression maybe-null à la variable, le compilateur émet un avertissement. Les variables qui sont non-nullables ont un null-state not-null par défaut. - nullable : vous pouvez attribuer une valeur
null
ou une expression maybe-null à la variable. Lorsque le null-state de la variable est maybe-null, le compilateur émet un avertissement si vous déréférencez la variable. Le null-state par défaut de la variable est maybe-null. - oblivious : vous pouvez attribuer une valeur
null
ou une expression maybe-null à la variable. Le compilateur n’émet pas d’avertissements lorsque vous déréférencez la variable, ou lorsque vous attribuez une expression maybe-null à la variable.
Le null-state oblivious et la nullabilité oblivious correspondent au comportement avant que les types références nullables aient été introduits. Ces valeurs sont utiles pendant la migration ou lorsque votre application utilise une bibliothèque qui n’a pas activé les types références nullables.
L’analyse de l’état Null et les annotations variables sont désactivées par défaut pour les projets existants, ce qui signifie que tous les types de référence continuent de pouvoir accepter la valeur Null. À compter de .NET 6, elles sont activées par défaut pour les nouveaux projets. Pour plus d’informations sur l’activation de ces fonctionnalités en déclarant un contexte d’annotation pouvant accepter la valeur Null, consultez Contextes pouvant accepter la valeur Null.
Le reste de cet article décrit comment ces trois zones de fonctionnalités fonctionnent pour produire des avertissements lorsque votre code peut déréférencer une valeur null
. Le déréférencement d’une variable signifie accéder à l’un de ses membres à l’aide de l’opérateur .
(point), comme illustré dans l’exemple suivant :
string message = "Hello, World!";
int length = message.Length; // dereferencing "message"
Lorsque vous déréférencez une variable dont la valeur est null
, le runtime lève une System.NullReferenceException.
Voici ce que vous allez apprendre :
- Analyse du null-state du compilateur : comment le compilateur détermine si une expression est not-null ou maybe-null.
- Attributs appliqués aux API qui fournissent davantage de contexte pour l’analyse du null-state du compilateur.
- Annotations de variables nullables qui fournissent des informations sur votre intention pour les variables. Les annotations sont utiles pour les champs afin de définir le null-state par défaut au début des méthodes de membres.
- Règles régissant les arguments de type générique. De nouvelles contraintes ont été ajoutées, car les paramètres de type peuvent être des types références ou des types valeurs. Le suffixe
?
est implémenté différemment pour les types valeurs nullables et les types références nullables. - Les contextes nullables vous aident à migrer des projets volumineux. Vous pouvez activer des contextes nullables ou des avertissements dans certaines parties de votre application lors de la migration. Après avoir traité d’autres avertissements, vous pouvez activer les types références nullables pour l’ensemble du projet.
Enfin, vous apprendrez les pièges connus de l’analyse du null-state dans les types et les tableaux struct
.
Vous pouvez également explorer ces concepts dans notre module Learn sur la sécurité des valeurs Null en C#.
analyse du null-state
Lorsque les types références nullables sont activés, l’analyse du null-state effectue le suivi du null-state des références. Une expression est soit not-null soit maybe-null. Le compilateur détermine qu’une variable est not-null de deux manières :
- Une valeur connue pour être not-null a été attribuée à la variable.
- La variable a été vérifiée par rapport à
null
et n’a pas été modifiée depuis cette vérification.
Quand les types références nullables ne sont pas activés, toutes les expressions ont le null-state oblivious. Le reste de la section décrit le comportement lorsque les types références nullables sont activés.
Toute variable que le compilateur n’a pas déterminée comme not-null est considérée comme maybe-null. L’analyse fournit des avertissements dans les situations où vous pouvez déréférencer accidentellement une valeur null
. Le compilateur produit des avertissements basés sur l’état Null.
- Lorsqu’une variable est not-null, cette variable peut être déréférencée en toute sécurité.
- Lorsqu’une variable est maybe-null, cette variable doit être vérifiée pour s’assurer qu’elle n’est pas
null
avant de la déréférencer.
Prenons l’exemple suivant :
string message = null;
// warning: dereference null.
Console.WriteLine($"The length of the message is {message.Length}");
var originalMessage = message;
message = "Hello, World!";
// No warning. Analysis determined "message" is not-null.
Console.WriteLine($"The length of the message is {message.Length}");
// warning!
Console.WriteLine(originalMessage.Length);
Dans l’exemple précédent, le compilateur détermine que message
est maybe-null lorsque le premier message est imprimé. Il n’y a pas d’avertissement pour le deuxième message. La dernière ligne de code produit un avertissement, car originalMessage
peut être null. L’exemple suivant montre une utilisation plus pratique pour parcourir une arborescence de nœuds jusqu’à la racine, en traitant chaque nœud pendant le parcours :
void FindRoot(Node node, Action<Node> processNode)
{
for (var current = node; current != null; current = current.Parent)
{
processNode(current);
}
}
Le code précédent ne génère aucun avertissement pour le déréférencement de la variable current
. L’analyse statique détermine que current
n’est jamais déréférencé lorsqu’il est maybe-null. La variable current
est vérifiée par rapport à null
avant que la variable current.Parent
ne soit accédée et avant de passer current
à l’action ProcessNode
. Les exemples précédents montrent comment le compilateur détermine l’état Null des variables locales lorsqu’elles sont initialisées, affectées ou comparées à null
.
L’analyse du null-state ne trace pas dans les méthodes appelées. Par conséquent, les champs initialisés dans une méthode d’assistance traditionnelle appelée par tous les constructeurs génèrent un avertissement avec le modèle suivant :
Le nom de la propriété non-nullable doit contenir une valeur non nulle lors de la sortie du constructeur.
Vous pouvez traiter ces avertissements à l’aide de l’une des deux manières suivantes : chaînage du constructeur ou attributs pouvant accepter la valeur Null sur la méthode d’assistance. Le code suivant montre un exemple de chaque. La classe Person
utilise un constructeur commun appelé par tous les autres constructeurs. La classe Student
a une méthode d’assistance annotée avec l’attribut System.Diagnostics.CodeAnalysis.MemberNotNullAttribute :
using System.Diagnostics.CodeAnalysis;
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public Person() : this("John", "Doe") { }
}
public class Student : Person
{
public string Major { get; set; }
public Student(string firstName, string lastName, string major)
: base(firstName, lastName)
{
SetMajor(major);
}
public Student(string firstName, string lastName) :
base(firstName, lastName)
{
SetMajor();
}
public Student()
{
SetMajor();
}
[MemberNotNull(nameof(Major))]
private void SetMajor(string? major = default)
{
Major = major ?? "Undeclared";
}
}
Remarque
Un certain nombre d’améliorations apportées à l’assignation définie et à l’analyse du null-state ont été ajoutées dans C# 10. Lorsque vous effectuez une mise à niveau vers C# 10, vous pouvez trouver moins d’avertissements pouvant accepter la valeur Null qui sont des faux positifs. Vous pouvez en savoir plus sur les améliorations apportées à la spécification des fonctionnalités pour les améliorations d’assignation définitives.
L’analyse de l’état des variables pouvant accepter la valeur Null et les avertissements générés par le compilateur vous aident à éviter les erreurs de programme en déréférencement null
. L’article sur la résolution des avertissements nullables fournit des techniques de correction des avertissements les plus susceptibles d’apparaître dans votre code.
Attributs sur les signatures d’API
L’analyse du null-state a besoin d’indications de la part des développeurs pour comprendre la sémantique des API. Certaines API fournissent des vérifications de la valeur Null et doivent modifier l’état Null d’une variable de maybe-null à not-null. D’autres API retournent des expressions qui sontnot-null ou maybe-null en fonction de l’état Null des arguments d’entrée. Prenez par exemple le code suivant, qui affiche un message en capitales :
void PrintMessageUpper(string? message)
{
if (!IsNull(message))
{
Console.WriteLine($"{DateTime.Now}: {message.ToUpper()}");
}
}
bool IsNull(string? s) => s == null;
En fonction de l’inspection, tout développeur considérerait ce code comme sûr et ne devrait pas générer d’avertissements. Toutefois, le compilateur ne sait pas que IsNull
fournit une vérification de valeur Null, et il émet un avertissement pour l’instruction message.ToUpper()
, considérant message
comme étant une variable maybe-null. Utilisez l’attribut NotNullWhen
pour corriger cet avertissement :
bool IsNull([NotNullWhen(false)] string? s) => s == null;
Cet attribut informe le compilateur que si IsNull
renvoie false
, le paramètre s
n’est pas null. Le compilateur modifie le null-state de message
en not-null à l’intérieur du bloc if (!IsNull(message)) {...}
. Aucun avertissement n’est émis.
Les attributs fournissent des informations détaillées sur le null-state des arguments, des valeurs renvoyées et des membres de l’instance d’objet utilisée pour appeler un membre. Vous trouverez plus d’informations sur chaque attribut dans l’article de référence de langage sur les attributs de référence pouvant accepter la valeur Null. À compter de .NET 5, toutes les API runtime .NET sont annotées. Vous améliorez l’analyse statique en annotant vos API pour fournir des informations sémantiques sur l’état Null des arguments et des valeurs renvoyées.
Annotations de variables pouvant accepter la valeur Null
L’analyse du null-state fournit une analyse robuste pour les variables locales. Le compilateur a besoin de plus d’informations de votre part pour les variables membres. Le compilateur a besoin d’informations supplémentaires pour définir le null-state de tous les champs au niveau du crochet d’ouverture d’un membre. L’un des constructeurs accessibles peut être utilisé pour initialiser l’objet. Si un champ membre peut être défini sur null
, le compilateur doit supposer que son état Null est maybe-null au début de chaque méthode.
Vous utilisez des annotations qui peuvent déclarer si une variable est un type de référence pouvant accepter la valeur Null ou un type de référence non nullable. Ces annotations constituent des instructions importantes concernant l’état Null pour les variables :
- Une référence n’est pas censée être null. L’état par défaut d’une variable de référence non-nullable est not-null. Le compilateur applique des règles qui garantissent que vous pouvez sans problème déréférencer ces variables sans avoir d’abord à vérifier qu’elles ne sont pas null :
- La variable doit être initialisée avec une valeur non null.
- La variable ne peut jamais se voir attribuer la valeur
null
. Le compilateur émet un avertissement lorsque le code affecte une expression maybe-null à une variable qui ne doit pas être null.
- Une référence peut être null. L’état par défaut d’une variable de référence pouvant accepter la valeur Null est not-null. Le compilateur applique des règles pour s’assurer que vous vérifiez correctement la présence d’une référence
null
:- Il se peut que la variable ne soit déréférencée que lorsque le compilateur peut garantir que la valeur n’est pas
null
. - Il se peut que ces variables soient initialisées avec la valeur
null
par défaut et que la valeurnull
leur soit attribuée dans d’autres codes. - Le compilateur n’émet pas d’avertissements lorsque le code affecte une expression maybe-null à une variable qui peut être null.
- Il se peut que la variable ne soit déréférencée que lorsque le compilateur peut garantir que la valeur n’est pas
Toute variable de référence non-nullable a un null-state not-null par défaut. Toute variable de référence nullable a initialement un null-state maybe-null.
Un type de référence nullable est inidqué avec la même syntaxe que celle des types valeur nullable : un ?
est ajouté au type de la variable. Par exemple, la déclaration de variable suivante représente une variable de chaîne nullable, name
:
string? name;
Lorsque les types références nullables sont activés, toute variable où le ?
n’est pas ajouté au nom de type est un type référence non-nullable. Cela inclut toutes les variables de type référence dans le code existant une fois que vous avez activé cette fonctionnalité. Toutefois, toutes les variables locales implicitement typées (déclarées à l’aide de var
) sont des types de référence pouvant accepter la valeur Null. Comme l’ont montré les sections précédentes, l’analyse statique détermine lenull-state des variables locales pour déterminer si elles sont maybe-null avant de les déréférencer.
Parfois, vous devez remplacer un avertissement quand vous savez qu’une variable n’est pas null, mais que le compilateur détermine que son état Null est maybe-null. Vous utilisez l’opérateur null-forgiving !
à la suite d’un nom de variable pour forcer l’état Null à être not-null. Par exemple, si vous savez que la variable name
n’est pas null
alors que le compilateur génère un avertissement, vous pouvez écrire le code suivant pour remplacer l’analyse du compilateur :
name!.Length;
Les types références et les types valeurs nullables fournissent un concept sémantique similaire : une variable peut représenter une valeur ou un objet, ou cette variable peut être null
. Toutefois, les types référence et les types valeur pouvant accepter la valeur Null sont implémentés différemment : les types valeur pouvant accepter la valeur Null sont implémentés à l’aide de System.Nullable<T>, et les types référence pouvant accepter la valeur Null sont implémentés par les attributs lus par le compilateur. Par exemple, string?
et string
sont tous deux représentés par le même type System.String. Toutefois, int?
et int
sont représentés par System.Nullable<System.Int32>
et System.Int32, respectivement.
Les types de référence pouvant accepter la valeur Null sont une fonctionnalité de compilation. Cela signifie qu’il est possible pour les appelants d’ignorer les avertissements et d’utiliser null
intentionnellement comme argument pour une méthode qui attend une référence non nullable. Les auteurs de bibliothèque doivent inclure des vérifications à l’exécution relatives aux valeurs d’argument null. ArgumentNullException.ThrowIfNull est l’option préférée pour vérifier un paramètre par rapport à la valeur Null au moment de l’exécution.
Important
L’activation des annotations nullables peut modifier la manière dont Entity Framework Core détermine si un membre de données est requis. Pour plus d’informations, consultez l’article Principes de base d’Entity Framework Core : Utilisation des types référence nullables.
Génériques
Les génériques nécessitent des règles détaillées pour gérer T?
pour tout paramètre de type T
. Les règles sont nécessairement détaillées en raison de l’historique et de l’implémentation différente d’un type valeur et d’un type référence pouvant accepter la valeur Null. Les types valeur pouvant accepter la valeur Null sont implémentés à l’aide du struct System.Nullable<T>. Les types référence pouvant accepter la valeur Null sont implémentés en tant qu’annotations de type qui fournissent des règles sémantiques au compilateur.
- Si l’argument de type pour
T
est un type référence,T?
fait référence au type de référence pouvant accepter la valeur Null correspondant. Par exemple, siT
est unstring
, alorsT?
est unstring?
. - Si l’argument de type pour
T
est un type valeur,T?
fait référence au même type valeur,T
. Par exemple, siT
est unint
,T?
est également unint
. - Si l’argument de type pour
T
est un type référence pouvant accepter la valeur Null,T?
fait référence au type de référence pouvant accepter la valeur Null correspondant. Par exemple, siT
est unstring?
, alorsT?
est aussi unstring?
. - Si l’argument de type pour
T
est un type valeur pouvant accepter la valeur Null,T?
fait référence au même type valeur pouvant accepter la valeur Null. Par exemple, siT
est unint?
, alorsT?
est aussi unint?
.
Pour les valeurs renvoyées, T?
équivaut à [MaybeNull]T
; pour les valeurs d’argument, T?
équivaut à [AllowNull]T
. Pour plus d’informations, consultez l’article Attributs pour l’analyse de l’état Null dans la référence de langage.
Vous pouvez spécifier un comportement différent à l’aide de contraintes :
- La contrainte
class
signifie queT
doit être un type référence non nullable (par exemplestring
). Le compilateur génère un avertissement si vous utilisez un type référence pouvant accepter la valeur Null, par exemplestring?
pourT
. - La contrainte
class?
signifie queT
doit être un type référence, non nullable (string
) ou un type référence pouvant accepter la valeur nulle (par exemplestring?
). Lorsque le paramètre de type est un type référence pouvant accepter la valeur Nul, tel questring?
, une expression deT?
fait référence à ce même type référence pouvant accepter la valeur Null, commestring?
. - La contrainte
notnull
signifie queT
doit être un type référence non nullable ou un type valeur non nullable. Si vous utilisez un type référence ou un type valeur pouvant accepter la valeur Null pour le paramètre de type, le compilateur génère un avertissement. En outre, quandT
est un type valeur, la valeur renvoyée est ce type valeur, et non le type de valeur pouvant accepter la valeur Null correspondant.
Ces contraintes permettent de fournir au compilateur plus d’informations sur la façon dont T
est utilisé. Cela est utile lorsque les développeurs choisissent le type pour T
, et cela fournit une meilleure analyse du null-state lorsqu’une instance du type générique est utilisée.
Contextes nullables
Pour les petits projets, vous pouvez activer les types références nullables, corriger les avertissements et continuer. Cela peut toutefois générer un grand nombre d’avertissements pour les projets de grande envergure et les solutions à plusieurs projets. Vous pouvez utiliser des pragmas pour activer les types références nullables fichier par fichier lorsque vous commencez à utiliser des types références nullables. Les nouvelles fonctionnalités qui protègent contre la levée d’une System.NullReferenceException peut être perturbante lorsqu’elles sont activées dans un codebase existant :
- Toutes les variables de référence explicitement typées sont interprétées comme des types référence non nullables.
- La signification de la contrainte
class
dans les génériques a changé pour désigner un type référence non nullable. - De nouveaux avertissements sont générés en raison de ces nouvelles règles.
Le contexte d’annotation nullable détermine le comportement du compilateur. Il existe quatre valeurs pour le contexte d’annotation nullable :
- disable : le code est nullable-oblivious. Désactiver correspond au comportement avant que les types références nullables n’aient été activés, sauf que la nouvelle syntaxe génère des avertissements au lieu d’erreurs.
- Les avertissements de valeur Null sont désactivés.
- Toutes les variables de type référence sont des types référence pouvant accepter la valeur Null.
- L’utilisation du suffixe
?
pour déclarer un type référence pouvant accepter la valeur Null génère un avertissement. - Vous pouvez utiliser l’opérateur null forgiving,
!
, mais il n’a aucun effet.
- enable : le compilateur active toutes les analyses de référence Null et toutes les fonctionnalités de langage.
- Tous les nouveaux avertissements nullables sont activés.
- Vous pouvez utiliser le suffixe
?
pour déclarer un type référence pouvant accepter la valeur Null. - Les variables de type référence sans le suffixe
?
sont des types références non-nullables. - L’opérateur null forgiving supprime les avertissements pour une affectation possible à
null
.
- warnings : le compilateur effectue toutes les analyses de valeurs Null et émet des avertissements quand le code peut déréférencer
null
.- Tous les nouveaux avertissements nullables sont activés.
- L’utilisation du suffixe
?
pour déclarer un type référence pouvant accepter la valeur Null génère un avertissement. - Toutes les variables de type référence sont autorisées à être null. Toutefois, les membres ont l’état Null de not-null à l’ouverture de toutes les méthodes, sauf s’ils sont déclarés avec le suffixe
?
. - Vous pouvez utiliser l’opérateur null forgiving,
!
.
- annotations : le compilateur n’émet pas d’avertissements lorsque le code peut déréférencer
null
, ou lorsque vous attribuez une expression maybe-null à une variable non-nullable.- Les nouveaux avertissements de valeur Null sont désactivés.
- Vous pouvez utiliser le suffixe
?
pour déclarer un type référence pouvant accepter la valeur Null. - Les variables de type référence sans le suffixe
?
sont des types références non-nullables. - Vous pouvez utiliser l’opérateur null forgiving,
!
, mais il n’a aucun effet.
Le contexte d’annotation nullable et le contexte d’avertissement nullable peuvent être définis pour un projet avec l’<Nullable>
élément de votre fichier .csproj. Cet élément configure la manière dont le compilateur interprète la nullabilité des types et quels avertissements sont générés. Le tableau suivant présente les valeurs autorisées et résume les contextes qu’elles spécifient.
Context | Avertissements de déréférencement | Avertissements d’assignation | Types référence | Suffixe ? |
L’opérateur ! |
---|---|---|---|---|---|
disable |
Désactivé | Désactivé | Tous acceptent la valeur Null | Génère un avertissement | N'a aucun effet |
enable |
activé | activé | Non nullable, sauf déclaré avec ? |
Déclare le type pouvant accepter la valeur Null | Supprime les avertissements en cas d’affectation null possible |
warnings |
activé | Non applicable | Tous acceptent la valeur Null, mais les membres sont considérés not-null au moment de l’ouverture des méthodes | Génère un avertissement | Supprime les avertissements en cas d’affectation null possible |
annotations |
Désactivé | Désactivé | Non nullable, sauf déclaré avec ? |
Déclare le type pouvant accepter la valeur Null | N'a aucun effet |
Les variables de type référence dans le code compilé dans un contexte désactivé sont nullable-oblivious. Vous pouvez assigner un littéral null
ou une variable maybe-null à une variable qui peut avoir la valeur nullable-oblivious. Toutefois, l’état par défaut d’une variable nullable-oblivious estnot-null.
Vous pouvez choisir le paramètre le mieux adapté à votre projet :
- Choisissez disable pour les projets hérités que vous ne souhaitez pas mettre à jour en fonction des diagnostics ou de nouvelles fonctionnalités.
- Choisissez warnings pour déterminer où votre code peut lever System.NullReferenceException. Vous pouvez résoudre ces avertissements avant de modifier le code pour activer les types référence non nullables.
- Choisissez annotations pour exprimer votre intention de conception avant d’activer les avertissements.
- Choisissez enable pour les nouveaux projets et les projets actifs pour lesquels vous souhaitez vous protéger contre les exceptions de référence Null.
Exemple :
<Nullable>enable</Nullable>
Vous pouvez aussi utiliser des directives pour définir ces mêmes contextes partout dans votre code source. Ces directives sont particulièrement utiles lorsque vous migrez un codebase volumineux.
#nullable enable
: Définit le contexte d’annotation nullable et le contexte d’avertissement nullable sur enable.#nullable disable
: Définit le contexte d’annotation nullable et le contexte d’avertissement nullable sur disable.#nullable restore
: Restaure le contexte d’annotation nullable et le contexte d’avertissement nullable avec les paramètres du projet.#nullable disable warnings
: Définit le contexte d’avertissement nullable sur disable.#nullable enable warnings
:Définit le contexte d’avertissement nullable sur enable.#nullable restore warnings
: Restaure le contexte d’avertissement nullable avec les paramètres du projet.#nullable disable annotations
: Définit le contexte d’annotation nullable sur disable.#nullable enable annotations
: Définit le contexte d’annotation nullable sur enable.#nullable restore annotations
: Restaure le contexte d’avertissement d’annotation avec les paramètres du projet.
Pour n’importe quelle ligne de code, vous pouvez définir l’une des combinaisons suivantes :
Contexte d’avertissement | Contexte des annotations | Utilisation |
---|---|---|
paramètres par défaut du projet | paramètres par défaut du projet | Default |
enable | disable | Corriger les avertissements liés à l'analyse du code |
enable | paramètres par défaut du projet | Corriger les avertissements liés à l'analyse du code |
paramètres par défaut du projet | enable | Ajouter des annotations de type |
enable | enable | Code déjà migré |
disable | enable | Annoter le code avant de corriger les avertissements |
disable | disable | Ajout du code hérité au projet migré |
paramètres par défaut du projet | disable | Rarement |
disable | paramètres par défaut du projet | Rarement |
Ces neuf combinaisons vous offrent un contrôle précis sur les diagnostics émis pour votre code par le compilateur. Vous pouvez activer davantage de fonctionnalités dans n’importe quelle zone que vous mettez à jour, sans voir d’autres avertissements que vous n’êtes pas encore prêt à traiter.
Important
Le contexte global pouvant accepter la valeur Null ne s’applique pas aux fichiers de code générés. Dans l’une ou l’autre stratégie, le contexte pouvant accepter la valeur Null est désactivé pour tout fichier source marqué comme généré. Cela signifie que des API dans les fichiers générés ne sont pas annotées. Il existe quatre façons de marquer un fichier comme généré :
- Dans .editorconfig, spécifiez
generated_code = true
dans une section qui s’applique à ce fichier. - Placez
<auto-generated/>
ou<auto-generated>
dans un commentaire en haut du fichier. Il peut se trouver sur n’importe quelle ligne de ce commentaire, mais le bloc de commentaires doit être le premier élément du fichier. - Le nom du fichier doit commencer par TemporaryGeneratedFile_.
- Le nom du fichier doit se terminer par .designer.cs, .generated.cs, .g.cs ou .g.i.cs.
Les générateurs peuvent s’inscrire à l’aide de la #nullable
directive du préprocesseur.
Les contextes d’annotation et d’avertissement nullables sont désactivés. Cela signifie que votre code existant compile sans rien changer et sans générer aucun nouvel avertissement. À partir de .NET 6, les nouveaux projets incluent l’élément <Nullable>enable</Nullable>
dans les modèles de projet.
Ces options fournissent deux stratégies distinctes pour mettre à jour un codebase existant afin d’utiliser des types référence pouvant accepter la valeur Null.
Pièges connus
Les tableaux et les structs qui contiennent des types référence sont des pièges connus dans les références pouvant accepter la valeur Null et l’analyse statique qui détermine la sécurité null. Dans les deux cas, une référence non-nullable peut être initialisée sur null
, sans générer d’avertissements.
Structures
Un struct qui contient des types référence non nullables autorise l’assignation de default
pour celui-ci sans avertissement. Prenons l’exemple suivant :
using System;
#nullable enable
public struct Student
{
public string FirstName;
public string? MiddleName;
public string LastName;
}
public static class Program
{
public static void PrintStudent(Student student)
{
Console.WriteLine($"First name: {student.FirstName.ToUpper()}");
Console.WriteLine($"Middle name: {student.MiddleName?.ToUpper()}");
Console.WriteLine($"Last name: {student.LastName.ToUpper()}");
}
public static void Main() => PrintStudent(default);
}
Dans l’exemple précédent, il n’y a aucun avertissement dans PrintStudent(default)
alors que les types de référence non nullables FirstName
et LastName
sont null.
Un autre cas plus commun concerne le traitement des structs génériques. Prenons l’exemple suivant :
#nullable enable
public struct S<T>
{
public T Prop { get; set; }
}
public static class Program
{
public static void Main()
{
string s = default(S<string>).Prop;
}
}
Dans l’exemple précédent, la propriété Prop
est null
au moment de l’exécution. Elle est attribuée à une chaîne non-nullable sans avertissements.
Tableaux
Les tableaux sont également un piège connu dans les types référence nullables. Prenons l’exemple suivant qui ne génère aucun avertissement :
using System;
#nullable enable
public static class Program
{
public static void Main()
{
string[] values = new string[10];
string s = values[0];
Console.WriteLine(s.ToUpper());
}
}
Dans l’exemple précédent, la déclaration du tableau montre qu’il contient des chaînes non nullables, tandis que ses éléments sont tous initialisés sur null
. Ensuite, une variable s
est affectée à la valeur null
(le premier élément du tableau). Enfin, la variable s
est déréférencée, ce qui cause une exception d’exécution.