Types référence nullables (Référence C#)

Notes

Cet article traite des types référence nullables. Vous pouvez également déclarer des types valeur nullables.

Les types référence nullables sont disponibles dans du code qui a opté pour un contexte prenant en charge les types nullables. Les types référence nullables, les avertissements d’analyse statique null et l’opérateur null-forgiving sont des fonctionnalités de langage facultatives. Ils sont tous désactivés par défaut. Un contexte nullable est contrôlé au niveau du projet à l’aide des paramètres de build ou dans le code à l’aide de pragmas.

Important

Tous les modèles de projet commençant par .NET 6 (C# 10) activent le contexte nullable pour le projet. Les projets créés avec des modèles antérieurs n’incluent pas cet élément, et ces fonctionnalités sont désactivées, sauf si vous les activez dans le fichier projet ou si vous utilisez des pragmas.

Dans un contexte prenant en charge les types nullables :

  • Une variable d’un type référence T doit être initialisée avec une valeur non-null et peut ne jamais être affectée d’une valeur pouvant être null.
  • Une variable d’un type référence T? peut être initialisée avec null ou se voir affecter null, mais doit être comparée à null avant d’être déréférencée.
  • Une variable m de type T? est considérée comme non-null lorsque vous appliquez l’opérateur null-forgiving, comme dans m!.

Les distinctions entre un type référence non nullable T et un type référence nullable T? sont appliquées par l’interprétation des règles précédentes par le compilateur. Une variable de type T et une variable de type T? sont représentées par le même type .NET. L’exemple suivant déclare une chaîne non nullable et une chaîne nullable, puis utilise l’opérateur null-forgiving pour affecter une valeur à une chaîne non nullable :

string notNull = "Hello";
string? nullable = default;
notNull = nullable!; // null forgiveness

Les variables notNull et nullable sont toutes les deux représentées par le type String. Étant donné que les types non nullables et nullables sont tous les deux stockés comme un même type, il existe plusieurs emplacements où l’utilisation d’un type référence nullable n’est pas autorisée. En général, un type référence nullable ne peut pas être utilisé comme classe de base ou interface implémentée. Un type référence nullable ne peut pas être utilisé dans une expression de création d’objet ni de test de type. Un type référence nullable ne peut pas être le type d’une expression d’accès au membre. Les exemples suivants illustrent ces constructions :

public MyClass : System.Object? // not allowed
{
}

var nullEmpty = System.String?.Empty; // Not allowed
var maybeObject = new object?(); // Not allowed
try
{
    if (thing is string? nullableString) // not allowed
        Console.WriteLine(nullableString);
} catch (Exception? e) // Not Allowed
{
    Console.WriteLine("error");
}

Références nullables et analyse statique

Les exemples de la section précédente illustrent la nature des types référence nullables. Les types référence nullables ne sont pas de nouveaux types de classes, mais plutôt des annotations sur des types référence existants. Le compilateur utilise ces annotations pour vous aider à trouver des erreurs de référence null potentielles dans votre code. Il n’existe aucune différence de runtime entre un type référence non nullable et un type référence nullable. Le compilateur n’ajoute aucune vérification de runtime pour les types référence non nullables. Les avantages se trouvent dans l’analyse au moment de la compilation. Le compilateur génère des avertissements qui vous aident à rechercher et à corriger les erreurs null potentielles dans votre code. Vous déclarez votre intention et le compilateur vous avertit lorsque votre code contrevient à cette intention.

Dans un contexte prenant en charge les types nullables, le compilateur effectue une analyse statique sur des variables de n’importe quel type référence, nullable et non nullable. Le compilateur effectue le suivi de l’état null de chaque variable de référence comme not-null ou maybe-null. L’état par défaut d’une référence non nullable est not-null. L’état par défaut d’une référence nullable est maybe-null.

Les types référence non nullables doivent toujours pouvoir être déréférencés en toute sécurité, car leur état null est not-null. Pour appliquer cette règle, le compilateur émet des avertissements si un type référence non nullable n’est pas initialisé en valeur non-null. Les variables locales doivent être affectées à l’endroit où elles sont déclarées. Chaque champ doit se voir attribuer une valeur not-null, dans un initialiseur de champ ou dans chaque constructeur. Le compilateur émet des avertissements quand une référence non nullable est affectée à une référence dont l’état est maybe-null. En règle générale, une référence non nullable est not-null et aucun avertissement n’est émis quand ces variables sont déréférencées.

Notes

Si vous affectez une expression maybe-null à un type de référence non nullable, le compilateur génère un avertissement. Le compilateur génère ensuite des avertissements pour cette variable jusqu’à ce qu’elle soit affectée à une expression not-null.

Les types référence nullables peuvent être initialisés ou affectés à null. Par conséquent, l’analyse statique doit déterminer qu’une variable est not-null avant d’être déréférencée. S’il est déterminé qu’une référence nullable est maybe-null, l’affectation à une variable de référence non nullable génère un avertissement du compilateur. La classe suivante montre des exemples de ces avertissements :

public class ProductDescription
{
    private string shortDescription;
    private string? detailedDescription;

    public ProductDescription() // Warning! shortDescription not initialized.
    {
    }

    public ProductDescription(string productDescription) =>
        this.shortDescription = productDescription;

    public void SetDescriptions(string productDescription, string? details=null)
    {
        shortDescription = productDescription;
        detailedDescription = details;
    }

    public string GetDescription()
    {
        if (detailedDescription.Length == 0) // Warning! dereference possible null
        {
            return shortDescription;
        }
        else
        {
            return $"{shortDescription}\n{detailedDescription}";
        }
    }

    public string FullDescription()
    {
        if (detailedDescription == null)
        {
            return shortDescription;
        }
        else if (detailedDescription.Length > 0) // OK, detailedDescription can't be null.
        {
            return $"{shortDescription}\n{detailedDescription}";
        }
        return shortDescription;
    }
}

L’extrait de code suivant montre où le compilateur émet des avertissements lors de l’utilisation de cette classe :

string shortDescription = default; // Warning! non-nullable set to null;
var product = new ProductDescription(shortDescription); // Warning! static analysis knows shortDescription maybe null.

string description = "widget";
var item = new ProductDescription(description);

item.SetDescriptions(description, "These widgets will do everything.");

Les exemples précédents montrent comment l’analyse statique du compilateur détermine l’état null des variables de référence. Le compilateur applique des règles de langage pour les contrôles de valeur null et les affectations afin de guider son analyse. Le compilateur ne peut pas faire d’hypothèses sur la sémantique des méthodes ni des propriétés. Si vous appelez des méthodes qui effectuent des contrôles de valeur null, le compilateur ne peut pas savoir que ces méthodes affectent l’état null d’une variable. Il existe des attributs que vous pouvez ajouter à vos API pour informer le compilateur sur la sémantique des arguments et des valeurs de retour. Ces attributs ont été appliqués à de nombreuses API courantes dans les bibliothèques .NET Core. Par exemple, IsNullOrEmpty a été mis à jour et le compilateur interprète correctement cette méthode comme un contrôle de valeur null. Pour plus d’informations sur les attributs qui s’appliquent à l’analyse statique d’état null, consultez l’article sur les attributs nullables.

Définition du contexte nullable

Il existe deux façons de contrôler le contexte nullable. Au niveau du projet, vous pouvez ajouter le paramètre de projet <Nullable>enable</Nullable>. Dans un fichier source C# individuel, vous pouvez ajouter le pragma #nullable enable pour activer le contexte nullable. Consultez l’article sur la définition d’une stratégie nullable. Avant .NET 6, les nouveaux projets utilisent la valeur par défaut, <Nullable>disable</Nullable>. À partir de .NET 6, les nouveaux projets incluent l’élément <Nullable>enable</Nullable> dans le fichier projet.

spécification du langage C#

Pour plus d’informations, consultez les propositions suivantes pour la spécification du langage C# :

Voir aussi