Mettre à jour les bibliothèques pour utiliser des types référence Nullable et communiquer des règles Nullable aux appelantsUpdate libraries to use nullable reference types and communicate nullable rules to callers

L’ajout de types de référence Nullable signifie que vous pouvez déclarer si une valeur null est autorisée ou non pour chaque variable.The addition of nullable reference types means you can declare whether or not a null value is allowed or expected for every variable. En outre, vous pouvez appliquer un certain nombre d’attributs : AllowNull, DisallowNull, MaybeNull, NotNull, NotNullWhen, MaybeNullWhenet NotNullWhenNotNull pour décrire complètement les États null de l’argument et les valeurs de retour.In addition, you can apply a number of attributes: AllowNull, DisallowNull, MaybeNull, NotNull, NotNullWhen, MaybeNullWhen, and NotNullWhenNotNull to completely describe the null states of argument and return values. Cela offre une excellente expérience lorsque vous écrivez du code.That provides a great experience as you write code. Vous recevez des avertissements si une variable qui n’accepte pas les valeurs NULL peut être définie sur null.You get warnings if a non-nullable variable might be set to null. Vous recevez des avertissements si une variable Nullable n’est pas vérifiée par null avant d’être déréférencée.You get warnings if a nullable variable isn't null-checked before you dereference it. La mise à jour de vos bibliothèques peut prendre du temps, mais les avantages le justifient.Updating your libraries can take time, but the payoffs are worth it. Plus d’informations que vous fournissez au compilateur sur le moment où une valeur null est autorisée ou interdite, les meilleurs avertissements que les utilisateurs de votre API obtiendront.The more information you provide to the compiler about when a null value is allowed or prohibited, the better warnings users of your API will get. Commençons par un exemple familier.Let's start with a familiar example. Imaginez que votre bibliothèque possède l’API suivante pour récupérer une chaîne de ressource :Imagine your library has the following API to retrieve a resource string:

bool TryGetMessage(string key, out string message)

L’exemple précédent suit le modèle de Try* familier dans .NET.The preceding example follows the familiar Try* pattern in .NET. Il existe deux arguments de référence pour cette API : le key et le paramètre message.There are two reference arguments for this API: the key and the message parameter. Cette API comporte les règles suivantes relatives à la valeur null de ces arguments :This API has the following rules relating to the nullness of these arguments:

  • Les appelants ne doivent pas passer null comme argument pour key.Callers shouldn't pass null as the argument for key.
  • Les appelants peuvent passer une variable dont la valeur est null en tant qu’argument pour message.Callers can pass a variable whose value is null as the argument for message.
  • Si la méthode TryGetMessage retourne true, la valeur de message n’est pas null.If the TryGetMessage method returns true, the value of message isn't null. Si la valeur de retour est false, la valeur de message (et son état null) est null.If the return value is false, the value of message (and its null state) is null.

La règle pour key peut être entièrement exprimée par le type de variable : key doit être un type de référence non Nullable.The rule for key can be completely expressed by the variable type: key should be a non-nullable reference type. Le paramètre message est plus complexe.The message parameter is more complex. Elle autorise null comme argument, mais garantit que, en cas de réussite, cet argument out n’est pas null.It allows null as the argument, but guarantees that, on success, that out argument isn't null. Pour ces scénarios, vous avez besoin d’un vocabulaire plus riche pour décrire les attentes.For these scenarios, you need a richer vocabulary to describe the expectations.

La mise à jour de votre bibliothèque pour les références Nullable nécessite plus que la désarrosage ? sur certaines variables et certains noms de types.Updating your library for nullable references requires more than sprinkling ? on some of the variables and type names. L’exemple précédent montre que vous devez examiner vos API et prendre en compte vos attentes pour chaque argument d’entrée.The preceding example shows that you need to examine your APIs and consider your expectations for each input argument. Prenez en compte les garanties pour la valeur de retour, ainsi que tout out ou ref arguments sur le retour de la méthode.Consider the guarantees for the return value, and any out or ref arguments upon the method's return. Ensuite, communiquez ces règles au compilateur et le compilateur fournira des avertissements quand les appelants ne respectent pas ces règles.Then communicate those rules to the compiler, and the compiler will provide warnings when callers don't abide by those rules.

Ce travail prend du temps.This work takes time. Commençons par des stratégies pour rendre votre bibliothèque ou votre application prenant en charge les valeurs NULL, tout en équilibrant d’autres exigences et livrables.Let's start with strategies to make your library or application nullable-aware, while balancing other requirements and deliverables. Vous verrez comment équilibrer le développement en cours et activer les types de référence Nullable.You'll see how to balance ongoing development enabling nullable reference types. Vous découvrirez les défis liés aux définitions de type générique.You'll learn challenges for generic type definitions. Vous apprendrez à appliquer des attributs pour décrire des conditions préalables et postérieures à des API individuelles.You'll learn to apply attributes to describe pre- and post-conditions on individual APIs.

Choisir une stratégie NullableChoose a nullable strategy

Le premier choix est si les types de référence Nullable doivent être activés ou désactivés par défaut.The first choice is whether nullable reference types should be on or off by default. Vous avez deux stratégies :You have two strategies:

  • Activez les types de référence Nullable pour l’ensemble du projet et désactivez-le dans le code qui n’est pas prêt.Enable nullable reference types for the entire project, and disable it in code that's not ready.
  • Activez uniquement les types référence Nullable pour le code qui a été annoté pour les types référence Nullable.Only enable nullable reference types for code that's been annotated for nullable reference types.

La première stratégie fonctionne mieux lorsque vous ajoutez d’autres fonctionnalités à la bibliothèque lors de sa mise à jour pour les types de référence Nullable.The first strategy works best when you're adding other features to the library as you update it for nullable reference types. Tout nouveau développement prend en charge les valeurs NULL.All new development is nullable aware. Lorsque vous mettez à jour le code existant, vous activez les types de référence Nullable dans ces classes.As you update existing code, you enable nullable reference types in those classes.

Après cette première stratégie, vous procédez comme suit :Following this first strategy, you do the following:

  1. Activez les types Nullable pour l’ensemble du projet en ajoutant l’élément <Nullable>enable</Nullable> à vos fichiers csproj .Enable nullable types for the entire project by adding the <Nullable>enable</Nullable> element to your csproj files.
  2. Ajoutez le pragma #nullable disable à chaque fichier source de votre projet.Add the #nullable disable pragma to every source file in your project.
  3. Lorsque vous travaillez sur chaque fichier, supprimez le pragma et résolvez tous les avertissements.As you work on each file, remove the pragma and address any warnings.

Cette première stratégie a plus de travail initial pour ajouter le pragma à chaque fichier.This first strategy has more up-front work to add the pragma to every file. L’avantage est que chaque nouveau fichier de code ajouté au projet sera activé pour la valeur null.The advantage is that every new code file added to the project will be nullable enabled. Tout nouveau travail prend en charge les valeurs null ; seul le code existant doit être mis à jour.Any new work will be nullable aware; only existing code must be updated.

La deuxième stratégie fonctionne mieux si la bibliothèque est généralement stable, et l’objectif principal du développement est d’adopter des types de référence Nullable.The second strategy works better if the library is generally stable, and the main focus of the development is to adopt nullable reference types. Vous activez les types de référence Nullable quand vous annotez des API.You turn on nullable reference types as you annotate APIs. Lorsque vous avez terminé, vous activez les types de référence Nullable pour l’ensemble du projet.When you've finished, you enable nullable reference types for the entire project.

Après cette seconde stratégie, vous effectuez les opérations suivantes :Following this second strategy you do the following:

  1. Ajoutez le pragma #nullable enable au fichier que vous souhaitez rendre compatible avec la valeur null.Add the #nullable enable pragma to the file you want to make nullable aware.
  2. Résolvez tous les avertissements.Address any warnings.
  3. Poursuivez ces deux premières étapes jusqu’à ce que vous ayez pris en charge la valeur null de la bibliothèque entière.Continue these first two steps until you've made the entire library nullable aware.
  4. Activez les types Nullable pour l’ensemble du projet en ajoutant l’élément <Nullable>enable</Nullable> à vos fichiers csproj .Enable nullable types for the entire project by adding the <Nullable>enable</Nullable> element to your csproj files.
  5. Supprimez les pragmas #nullable enable, car ils ne sont plus nécessaires.Remove the #nullable enable pragmas, as they're no longer needed.

Cette deuxième stratégie a moins de travail avant.This second strategy has less work up-front. Le compromis est que la première tâche lorsque vous créez un nouveau fichier consiste à ajouter le pragma et à le rendre compatible avec la valeur null.The tradeoff is that the first task when you create a new file is to add the pragma and make it nullable aware. Si des développeurs de votre équipe oublient, ce nouveau code est désormais dans le backlog de travail pour rendre tout le code prenant en charge la valeur null.If any developers on your team forget, that new code is now in the backlog of work to make all code nullable aware.

Les stratégies que vous choisissez dépendent de la quantité de développement actif dans votre projet.Which of these strategies you pick depends on how much active development is taking place in your project. Plus votre projet est mature et stable, plus la seconde stratégie est performante.The more mature and stable your project, the better the second strategy. Plus vous développez de fonctionnalités, mieux la première stratégie.The more features being developed, the better the first strategy.

Les avertissements de type Nullable introduisent-ils des modifications avec rupture ?Should nullable warnings introduce breaking changes?

Avant d’activer les types de référence Nullable, les variables sont considérées comme Nullable oublie.Before you enable nullable reference types, variables are considered nullable oblivious. Une fois que vous activez les types de référence Nullable, toutes ces variables ne peuvent pas avoir la valeur null.Once you enable nullable reference types, all those variables are non-nullable. Le compilateur émet des avertissements si ces variables ne sont pas initialisées à des valeurs non null.The compiler will issue warnings if those variables aren't initialized to non-null values.

Une autre source probable d’avertissements est celle des valeurs de retour lorsque la valeur n’a pas été initialisée.Another likely source of warnings is return values when the value hasn't been initialized.

La première étape dans la résolution des avertissements du compilateur consiste à utiliser des annotations ? sur les types de paramètres et de retour pour indiquer quand les arguments ou les valeurs de retour peuvent être null.The first step in addressing the compiler warnings is to use ? annotations on parameter and return types to indicate when arguments or return values may be null. Lorsque les variables de référence ne doivent pas avoir la valeur null, la déclaration d’origine est correcte.When reference variables must not be null, the original declaration is correct. Dans ce cas, votre objectif n’est pas simplement de corriger les avertissements.As you do this, your goal isn't just to fix warnings. L’objectif le plus important est de faire en sorte que le compilateur comprenne votre intention pour les valeurs NULL potentielles.The more important goal is to make the compiler understand your intent for potential null values. Lorsque vous examinez les avertissements, vous atteignez votre prochaine décision majeure pour votre bibliothèque.As you examine the warnings, you reach your next major decision for your library. Voulez-vous envisager de modifier les signatures d’API pour communiquer plus clairement votre intention de conception ?Do you want to consider modifying API signatures to more clearly communicate your design intent? Une meilleure signature d’API pour la méthode TryGetMessage examinée précédemment peut être :A better API signature for the TryGetMessage method examined earlier could be:

string? TryGetMessage(string key);

La valeur de retour indique la réussite ou l’échec et transporte la valeur si la valeur a été trouvée.The return value indicates success or failure, and carries the value if the value was found. Dans de nombreux cas, la modification des signatures d’API peut améliorer la façon dont elles communiquent les valeurs NULL.In many cases, changing API signatures can improve how they communicate null values.

Toutefois, pour les bibliothèques publiques ou les bibliothèques avec des bases d’utilisateurs volumineuses, vous préférerez peut-être ne pas introduire de modifications de signature d’API.However, for public libraries, or libraries with large user bases, you may prefer not introducing any API signature changes. Dans ces cas-là, et d’autres modèles courants, vous pouvez appliquer des attributs pour définir plus clairement quand un argument ou une valeur de retour peut être null.For those cases, and other common patterns, you can apply attributes to more clearly define when an argument or return value may be null. Si vous envisagez de modifier la surface de votre API, vous constaterez sans doute que les annotations de type ne suffisent pas pour décrire les valeurs de null pour les arguments ou les valeurs de retour.Whether or not you consider changing the surface of your API, you'll likely find that type annotations alone aren't sufficient for describing null values for arguments or return values. Dans ces instances, vous pouvez appliquer des attributs pour décrire plus clairement une API.In those instances, you can apply attributes to more clearly describe an API.

Les attributs étendent les annotations de typeAttributes extend type annotations

Plusieurs attributs ont été ajoutés pour exprimer des informations supplémentaires sur l’État null des variables.Several attributes have been added to express additional information about the null state of variables. Tout le code que vous C# avez écrit avant 8 introduit les types de référence Nullable était null oublie.All code you wrote before C# 8 introduced nullable reference types was null oblivious. Cela signifie que toute variable de type référence peut avoir la valeur null, mais que les vérifications de valeur NULL ne sont pas requises.That means any reference type variable may be null, but null checks aren't required. Une fois que votre code prend en charge les valeurs NULL, ces règles changent.Once your code is nullable aware, those rules change. Les types référence ne doivent jamais être la valeur null, et les types référence Nullable doivent être vérifiés par rapport à null avant d’être déréférencés.Reference types should never be the null value, and nullable reference types must be checked against null before being dereferenced.

Les règles de vos API sont probablement plus compliquées, comme vous l’avez vu dans le scénario d’API TryGetValue.The rules for your APIs are likely more complicated, as you saw with the TryGetValue API scenario. La plupart de vos API ont des règles plus complexes pour le moment où les variables peuvent ou ne peuvent pas être null.Many of your APIs have more complex rules for when variables can or can't be null. Dans ce cas, vous allez utiliser l’un des attributs suivants pour exprimer ces règles :In these cases, you'll use one of the following attributes to express those rules:

  • AllowNull a: un argument d’entrée qui n’accepte pas les valeurs NULL peut être null.AllowNull: A non-nullable input argument may be null.
  • DisallowNull: un argument d’entrée Nullable ne doit jamais avoir la valeur null.DisallowNull: A nullable input argument should never be null.
  • MaybeNull: une valeur de retour qui n’accepte pas les valeurs NULL peut être null.MaybeNull: A non-nullable return value may be null.
  • NOTNULL: une valeur de retour Nullable ne sera jamais null.NotNull: A nullable return value will never be null.
  • MaybeNullWhen: un argument d’entrée qui n’accepte pas les valeurs NULL peut être NULL lorsque la méthode retourne la valeur de bool spécifiée.MaybeNullWhen: A non-nullable input argument may be null when the method returns the specified bool value.
  • NotNullWhen: un argument d’entrée Nullable ne sera pas NULL lorsque la méthode retourne la valeur de bool spécifiée.NotNullWhen: A nullable input argument will not be null when the method returns the specified bool value.
  • NotNullIfNotNull: une valeur de retour n’est pas null si l’argument pour le paramètre spécifié n’est pas null.NotNullIfNotNull: A return value isn't null if the argument for the specified parameter isn't null.

Les descriptions précédentes sont une référence rapide à ce que fait chaque attribut.The preceding descriptions are a quick reference to what each attribute does. Chaque section suivante décrit le comportement et la signification de manière plus approfondie.Each following section describes the behavior and meaning more thoroughly.

L’ajout de ces attributs donne au compilateur plus d’informations sur les règles de votre API.Adding these attributes gives the compiler more information about the rules for your API. Quand le code appelant est compilé dans un contexte compatible avec la valeur null, le compilateur avertit les appelants lorsqu’ils enfreignent ces règles.When calling code is compiled in a nullable enabled context, the compiler will warn callers when they violate those rules. Ces attributs n’autorisent pas de vérifications supplémentaires sur votre implémentation.These attributes don't enable additional checks on your implementation.

Spécifier les conditions préalables : AllowNull et DisallowNullSpecify preconditions: AllowNull and DisallowNull

Prenons l’exemple d’une propriété en lecture/écriture qui ne retourne jamais null, car elle a une valeur par défaut raisonnable.Consider a read/write property that never returns null because it has a reasonable default value. Les appelants passent null à l’accesseur Set lors de sa définition sur cette valeur par défaut.Callers pass null to the set accessor when setting it to that default value. Par exemple, imaginez un système de messagerie qui demande un nom d’écran dans une salle de conversation.For example, consider a messaging system that asks for a screen name in a chat room. Si aucune valeur n’est fournie, le système génère un nom aléatoire :If none is provided, the system generates a random name:

public string ScreenName
{
   get => screenName;
   set => screenName = value ?? GenerateRandomScreenName();
}
private string screenName;

Quand vous compilez le code précédent dans un contexte oublie Nullable, tout est parfait.When you compile the preceding code in a nullable oblivious context, everything is fine. Une fois que vous activez les types de référence Nullable, la propriété ScreenName devient une référence qui n’accepte pas les valeurs NULL.Once you enable nullable reference types, the ScreenName property becomes a non-nullable reference. C’est correct pour l’accesseur get : il ne retourne jamais null.That's correct for the get accessor: it never returns null. Les appelants n’ont pas besoin de vérifier la propriété retournée pour null.Callers don't need to check the returned property for null. Mais maintenant, le paramétrage de la propriété sur null génère un avertissement.But now setting the property to null generates a warning. Afin de continuer à prendre en charge ce type de code, vous ajoutez l’attribut System.Diagnostics.CodeAnalysis.AllowNullAttribute à la propriété, comme indiqué dans le code suivant :In order to continue to support this type of code, you add the System.Diagnostics.CodeAnalysis.AllowNullAttribute attribute to the property, as shown in the following code:

[AllowNull]
public string ScreenName
{
   get => screenName;
   set => screenName = value ?? GenerateRandomScreenName();
}
private string screenName = GenerateRandomScreenName();

Vous devrez peut-être ajouter une directive using pour System.Diagnostics.CodeAnalysis pour l’utiliser, ainsi que d’autres attributs abordés dans cet article.You may need to add a using directive for System.Diagnostics.CodeAnalysis to use this and other attributes discussed in this article. L’attribut est appliqué à la propriété, et non à l’accesseur set.The attribute is applied to the property, not the set accessor. L’attribut AllowNull spécifie des conditions préalableset s’applique uniquement aux entrées.The AllowNull attribute specifies pre-conditions, and only applies to inputs. L’accesseur get a une valeur de retour, mais aucun argument d’entrée.The get accessor has a return value, but no input arguments. Par conséquent, l’attribut AllowNull s’applique uniquement à l’accesseur set.Therefore, the AllowNull attribute only applies to the set accessor.

L’exemple précédent montre ce qu’il faut rechercher lors de l’ajout de l’attribut AllowNull sur un argument :The preceding example demonstrates what to look for when adding the AllowNull attribute on an argument:

  1. Le contrat général pour cette variable est qu’elle ne doit pas être null, donc vous souhaitez un type de référence qui n’accepte pas les valeurs NULL.The general contract for that variable is that it shouldn't be null, so you want a non-nullable reference type.
  2. Il existe des scénarios pour la variable d’entrée à null, bien qu’il ne s’agisse pas de l’utilisation la plus courante.There are scenarios for the input variable to be null, though they aren't the most common usage.

La plupart du temps, vous aurez besoin de cet attribut pour les propriétés ou les arguments in, outet ref.Most often you'll need this attribute for properties, or in, out, and ref arguments. L’attribut AllowNull est le meilleur choix lorsqu’une variable est généralement non null, mais que vous devez autoriser null comme condition préalable.The AllowNull attribute is the best choice when a variable is typically non-null, but you need to allow null as a precondition.

À l’inverse, avec les scénarios d’utilisation de DisallowNull: vous utilisez cet attribut pour spécifier qu’une variable d’entrée d’un type Nullable ne doit pas être null.Contrast that with scenarios for using DisallowNull: You use this attribute to specify that an input variable of a nullable type shouldn't be null. Prenons l’exemple d’une propriété où null est la valeur par défaut, mais les clients peuvent uniquement la définir sur une valeur non null.Consider a property where null is the default value, but clients can only set it to a non-null value. Examinons le code ci-dessous.Consider the following code:

public string ReviewComment
{
    get => _comment;
    set => _comment = value ?? throw new ArgumentNullException(nameof(value), "Cannot set to null");
}
string _comment;

Le code précédent est la meilleure façon d’exprimer votre conception que le ReviewComment peut être null, mais ne peut pas être défini sur null.The preceding code is the best way to express your design that the ReviewComment could be null, but can't be set to null. Une fois que ce code prend en charge les valeurs NULL, vous pouvez exprimer ce concept plus clairement aux appelants à l’aide de la System.Diagnostics.CodeAnalysis.DisallowNullAttribute:Once this code is nullable aware, you can express this concept more clearly to callers using the System.Diagnostics.CodeAnalysis.DisallowNullAttribute:

[DisallowNull] 
public string? ReviewComment
{
    get => _comment;
    set => _comment = value ?? throw new ArgumentNullException(nameof(value), "Cannot set to null");
}
string? _comment;

Dans un contexte Nullable, l’accesseur get ReviewComment peut retourner la valeur par défaut de null.In a nullable context, the ReviewComment get accessor could return the default value of null. Le compilateur vous avertit qu’il doit être vérifié avant l’accès.The compiler warns that it must be checked before access. En outre, il avertit les appelants que, même s’il peut être null, les appelants ne doivent pas le définir explicitement sur null.Furthermore, it warns callers that, even though it could be null, callers shouldn't explicitly set it to null. L’attribut DisallowNull spécifie également une condition préalable, il n’affecte pas l’accesseur get.The DisallowNull attribute also specifies a pre-condition, it does not affect the get accessor. Vous devez choisir d’utiliser l’attribut DisallowNull lorsque vous observez ces caractéristiques sur :You should choose to use the DisallowNull attribute when you observe these characteristics about:

  1. La variable peut être null dans les scénarios de base, souvent lors de la première instanciation.The variable could be null in core scenarios, often when first instantiated.
  2. La variable ne doit pas être explicitement définie sur null.The variable shouldn't be explicitly set to null.

Ces situations sont courantes dans le code qui a été initialement null oublie.These situations are common in code that was originally null oblivious. Les propriétés de l’objet sont peut-être définies dans deux opérations d’initialisation distinctes.It may be that object properties are set in two distinct initialization operations. Il se peut que certaines propriétés soient définies uniquement après la fin d’un travail asynchrone.It may be that some properties are set only after some asynchronous work has completed.

Les attributs AllowNull et DisallowNull vous permettent de spécifier que les conditions préalables sur les variables peuvent ne pas correspondre aux annotations Nullable sur ces variables.The AllowNull and DisallowNull attributes enable you to specify that preconditions on variables may not match the nullable annotations on those variables. Celles-ci fournissent des informations plus détaillées sur les caractéristiques de votre API.These provide more detail about the characteristics of your API. Ces informations supplémentaires permettent aux appelants d’utiliser correctement votre API.This additional information helps callers use your API correctly. N’oubliez pas de spécifier des conditions préalables à l’aide des attributs suivants :Remember you specify preconditions using the following attributes:

  • AllowNull a: un argument d’entrée qui n’accepte pas les valeurs NULL peut être null.AllowNull: A non-nullable input argument may be null.
  • DisallowNull: un argument d’entrée Nullable ne doit jamais avoir la valeur null.DisallowNull: A nullable input argument should never be null.

Spécifier les conditions postérieures : MaybeNull et NotNullSpecify post-conditions: MaybeNull and NotNull

Supposons que vous avez une méthode avec la signature suivante :Suppose you have a method with the following signature:

public Customer FindCustomer(string lastName, string firstName)

Vous avez probablement écrit une méthode comme celle-ci pour retourner null lorsque le nom recherché n’a pas été trouvé.You've likely written a method like this to return null when the name sought wasn't found. La null indique clairement que l’enregistrement est introuvable.The null clearly indicates that the record wasn't found. Dans cet exemple, il est probable que vous modifiez le type de retour de Customer en Customer?.In this example, you'd likely change the return type from Customer to Customer?. La déclaration de la valeur de retour en tant que type référence Nullable spécifie clairement l’objectif de cette API.Declaring the return value as a nullable reference type specifies the intent of this API clearly.

Pour les raisons couvertes par les définitions génériques et la possibilité de valeur null , cette technique ne fonctionne pas avec les méthodes génériques.For reasons covered under Generic definitions and nullability that technique does not work with generic methods. Vous pouvez avoir une méthode générique qui suit un modèle similaire :You may have a generic method that follows a similar pattern:

public T Find<T>(IEnumerable<T> sequence, Func<T, bool> match)

Vous ne pouvez pas spécifier que la valeur de retour est T?.You can't specify that the return value is T?. La méthode retourne null lorsque l’élément recherché est introuvable.The method returns null when the sought item isn't found. Étant donné que vous ne pouvez pas déclarer un type de retour T?, vous ajoutez l’annotation MaybeNull au retour de la méthode :Since you can't declare a T? return type, you add the MaybeNull annotation to the method return:

[return: MaybeNull]
public T Find<T>(IEnumerable<T> sequence, Func<T, bool> match)

Le code précédent informe les appelants que le contrat implique un type non Nullable, mais la valeur de retour peut en fait être null.The preceding code informs callers that the contract implies a non-nullable type, but the return value may actually be null. Utilisez l’attribut MaybeNull lorsque votre API doit être un type non Nullable, en général un paramètre de type générique, mais il peut y avoir des instances où les null seraient retournées.Use the MaybeNull attribute when your API should be a non-nullable type, typically a generic type parameter, but there may be instances where null would be returned.

Vous pouvez également spécifier qu’une valeur de retour ou un argument out ou ref n’est pas null, même si le type est un type Nullable.You can also specify that a return value or an out or ref argument isn't null even though the type is a nullable type. Prenons l’exemple d’une méthode qui garantit qu’un tableau est suffisamment grand pour contenir un certain nombre d’éléments.Consider a method that ensures an array is large enough to hold a number of elements. Si l’argument d’entrée n’a pas de capacité, la routine alloue un nouveau tableau et copie tous les éléments existants dans celui-ci.If the input argument doesn't have capacity, the routine would allocate a new array and copy all the existing elements into it. Si l’argument d’entrée est null, la routine alloue un nouveau stockage.If the input argument is null, the routine would allocate new storage. Si la capacité est suffisante, la routine ne fait rien :If there's sufficient capacity, the routine does nothing:

public void EnsureCapacity<T>(ref T[] storage, int size)

Vous pouvez appeler cette routine comme suit :You could call this routine as follows:

// messages has the default value (null) when EnsureCapacity is called:
EnsureCapacity<string>(ref messages, 10);
// messages is not null.
EnsureCapacity<string>(messages, 50);

Après avoir activé les types référence null, vous souhaitez vous assurer que le code précédent se compile sans avertissements.After enabling null reference types, you want to ensure that the preceding code compiles without warnings. Lorsque la méthode retourne, l’argument storage est garanti comme non null.When the method returns, the storage argument is guaranteed to be not null. Toutefois, il est acceptable d’appeler EnsureCapacity avec une référence null.However, it's acceptable to call EnsureCapacity with a null reference. Vous pouvez créer storage un type de référence Nullable et ajouter le NotNull après condition à la déclaration du paramètre :You can make storage a nullable reference type, and add the NotNull post-condition to the parameter declaration:

public void EnsureCapacity<T>([NotNull]ref T[]? storage, int size)

Le code précédent exprime très clairement le contrat existant : les appelants peuvent passer une variable avec la valeur null, mais la valeur de retour est garantie pour ne jamais avoir la valeur null.The preceding code expresses the existing contract very clearly: Callers can pass a variable with the null value, but the return value is guaranteed to never be null. L’attribut NotNull est particulièrement utile pour les arguments ref et out lorsque null peut être passé comme argument, mais que cet argument est garanti comme n’étant pas NULL lorsque la méthode est retournée.The NotNull attribute is most useful for ref and out arguments where null may be passed as an argument, but that argument is guaranteed to be not null when the method returns.

Vous spécifiez des post-conditions inconditionnelles à l’aide des attributs suivants :You specify unconditional postconditions using the following attributes:

  • MaybeNull: une valeur de retour qui n’accepte pas les valeurs NULL peut être null.MaybeNull: A non-nullable return value may be null.
  • NOTNULL: une valeur de retour Nullable ne sera jamais null.NotNull: A nullable return value will never be null.

Spécifier les conditions de publication conditionnelles : NotNullWhen, MaybeNullWhenet NotNullIfNotNullSpecify conditional post-conditions: NotNullWhen, MaybeNullWhen, and NotNullIfNotNull

Vous êtes probablement familiarisé avec la méthode string String.IsNullOrEmpty(String).You're likely familiar with the string method String.IsNullOrEmpty(String). Cette méthode retourne true lorsque l’argument a la valeur null ou est une chaîne vide.This method returns true when the argument is null or an empty string. Il s’agit d’une forme de contrôle de valeur NULL : les appelants n’ont pas besoin de vérifier la valeur null si la méthode retourne false.It's a form of null-check: Callers don't need to null-check the argument if the method returns false. Pour rendre une méthode comme cette prise en charge de Nullable, vous devez définir l’argument sur un type Nullable et ajouter l’attribut NotNullWhen :To make a method like this nullable aware, you'd set the argument to a nullable type, and add the NotNullWhen attribute:

bool IsNullOrEmpty([NotNullWhen(false)]string? value);

Cela indique au compilateur que tout code dont la valeur de retour est false n’a pas besoin d’être vérifié par null.That informs the compiler that any code where the return value is false need not be null-checked. L’ajout de l’attribut indique à l’analyse statique du compilateur que IsNullOrEmpty effectue la vérification de la valeur null nécessaire : lorsqu’il retourne false, l’argument d’entrée n’est pas null.The addition of the attribute informs the compiler's static analysis that IsNullOrEmpty performs the necessary null check: when it returns false, the input argument is not null.

string? userInput = GetUserInput();
if (!string.IsNullOrEmpty(userInput))
{
   int messageLength = userInput.Length; // no null check needed.
}
// null check needed on userInput here.

La méthode String.IsNullOrEmpty(String) est annotée comme indiqué ci-dessus pour .NET Core 3,0.The String.IsNullOrEmpty(String) method will be annotated as shown above for .NET Core 3.0. Vous pouvez avoir des méthodes similaires dans votre code base, qui vérifient l’état des objets pour les valeurs NULL.You may have similar methods in your codebase that check the state of objects for null values. Le compilateur ne reconnaîtra pas les méthodes de vérification null personnalisées, et vous devrez ajouter vous-même les annotations.The compiler won't recognize custom null check methods, and you'll need to add the annotations yourself. Lorsque vous ajoutez l’attribut, l’analyse statique du compilateur sait quand la variable testée a été vérifiée avec la valeur null.When you add the attribute, the compiler's static analysis knows when the tested variable has been null checked.

Une autre utilisation de ces attributs est le modèle de Try*.Another use for these attributes is the Try* pattern. Les post-conditions pour les variables ref et out sont communiquées par le biais de la valeur de retour.The postconditions for ref and out variables are communicated through the return value. Prenons l’exemple de cette méthode ci-dessus :Consider this method shown earlier:

bool TryGetMessage(string key, out string message)

La méthode précédente suit un idiome .NET typique : la valeur de retour indique si message a été défini sur la valeur trouvée ou, si aucun message n’est trouvé, à la valeur par défaut.The preceding method follows a typical .NET idiom: the return value indicates if message was set to the found value or, if no message is found, to the default value. Si la méthode retourne true, la valeur de message n’est pas null ; dans le cas contraire, la méthode affecte la valeur null à message.If the method returns true, the value of message isn't null; otherwise, the method sets message to null.

Vous pouvez communiquer cet idiome à l’aide de l’attribut NotNullWhen.You can communicate that idiom using the NotNullWhen attribute. Quand vous mettez à jour la signature pour les types de référence Nullable, effectuez message un string? et ajoutez un attribut :When you update the signature for nullable reference types, make message a string? and add an attribute:

bool TryGetMessage(string key, [NotNullWhen(true)] out string? message)

Dans l’exemple précédent, la valeur de message est dite NOT NULL lorsque TryGetMessage retourne true.In the preceding example, the value of message is known to be not null when TryGetMessage returns true. Vous devez annoter des méthodes similaires dans votre code base de la même façon : les arguments peuvent être null, et sont connus pour ne pas avoir la valeur NULL lorsque la méthode retourne true.You should annotate similar methods in your codebase in the same way: the arguments could be null, and are known to be not null when the method returns true.

Vous pouvez également avoir un attribut final.There's one final attribute you may also need. Parfois, l’État null d’une valeur de retour dépend de l’État null d’un ou plusieurs arguments d’entrée.Sometimes the null state of a return value depends on the null state of one or more input arguments. Ces méthodes retournent une valeur non null chaque fois que certains arguments d’entrée ne sont pas null.These methods will return a non-null value whenever certain input arguments aren't null. Pour annoter correctement ces méthodes, vous utilisez l’attribut NotNullIfNotNull.To correctly annotate these methods, you use the NotNullIfNotNull attribute. Considérez la méthode suivante :Consider the following method:

string GetTopLevelDomainFromFullUrl(string url);

Si l’argument url n’a pas la valeur null, la sortie n’est pas null.If the url argument isn't null, the output isn't null. Une fois que les références Nullable sont activées, cette signature fonctionne correctement, à condition que votre API n’accepte jamais une entrée null.Once nullable references are enabled, that signature works correctly, provided your API never accepts a null input. Toutefois, si l’entrée peut être null, la valeur de retour peut également être null.However, if the input could be null, then return value could also be null. Par conséquent, vous pouvez remplacer la signature par le code suivant :Therefore, you could change the signature to the following code:

string? GetTopLevelDomainFromFullUrl(string? url);

Cela fonctionne également, mais obligent souvent les appelants à implémenter des contrôles de null supplémentaires.That also works, but will often force callers to implement extra null checks. Le contrat est que la valeur de retour est null uniquement lorsque l’argument d’entrée url est null.The contract is that the return value would be null only when the input argument url is null. Pour exprimer ce contrat, vous devez annoter cette méthode comme indiqué dans le code suivant :To express that contract, you would annotate this method as shown in the following code:

[return: NotNullIfNotNull("url")]
string? GetTopLevelDomainFromFullUrl(string? url);

La valeur de retour et l’argument ont tous deux été annotés avec le ? indiquant que peut être null.The return value and the argument have both been annotated with the ? indicating that either could be null. L’attribut clarifie davantage que la valeur de retour n’est pas NULL lorsque l’argument url n’est pas null.The attribute further clarifies that the return value won't be null when the url argument isn't null.

Vous spécifiez des post-conditions conditionnelles à l’aide des attributs suivants :You specify conditional postconditions using these attributes:

  • MaybeNullWhen: un argument d’entrée qui n’accepte pas les valeurs NULL peut être NULL lorsque la méthode retourne la valeur de bool spécifiée.MaybeNullWhen: A non-nullable input argument may be null when the method returns the specified bool value.
  • NotNullWhen: un argument d’entrée Nullable ne sera pas NULL lorsque la méthode retourne la valeur de bool spécifiée.NotNullWhen: A nullable input argument will not be null when the method returns the specified bool value.
  • NotNullIfNotNull: une valeur de retour n’est pas null si l’argument d’entrée pour le paramètre spécifié n’est pas null.NotNullIfNotNull: A return value isn't null if the input argument for the specified parameter isn't null.

Définitions génériques et possibilité de valeur nullGeneric definitions and nullability

La communication correcte de l’État null des types génériques et des méthodes génériques requiert une attention particulière.Correctly communicating the null state of generic types and generic methods requires special care. Cela découle du fait qu’un type valeur Nullable et un type référence Nullable sont fondamentalement différents.This stems from the fact that a nullable value type and a nullable reference type are fundamentally different. Un int? est un synonyme de Nullable<int>, tandis que string? est string avec un attribut ajouté par le compilateur.An int? is a synonym for Nullable<int>, whereas string? is string with an attribute added by the compiler. Le résultat est que le compilateur ne peut pas générer de code correct pour T? sans savoir si T est un class ou un struct.The result is that the compiler can't generate correct code for T? without knowing if T is a class or a struct.

Cela ne signifie pas que vous ne pouvez pas utiliser un type Nullable (type valeur ou type référence) comme argument de type pour un type générique fermé.This doesn't mean you can't use a nullable type (either value type or reference type) as the type argument for a closed generic type. List<string?> et List<int?> sont des instanciations valides de List<T>.Both List<string?> and List<int?> are valid instantiations of List<T>.

Cela signifie que vous ne pouvez pas utiliser T? dans une déclaration de classe ou de méthode générique sans contraintes.What it does mean is that you can't use T? in a generic class or method declaration without constraints. Par exemple, Enumerable.FirstOrDefault<TSource>(IEnumerable<TSource>) ne sera pas modifié pour retourner T?.For example, Enumerable.FirstOrDefault<TSource>(IEnumerable<TSource>) won't be changed to return T?. Vous pouvez contourner cette limitation en ajoutant la contrainte struct ou class.You can overcome this limitation by adding either the struct or class constraint. Avec l’une de ces contraintes, le compilateur sait comment générer du code pour les T et les T?.With either of those constraints, the compiler knows how to generate code for both T and T?.

Vous pouvez restreindre les types utilisés pour un argument de type générique à des types non nullables.You may want to restrict the types used for a generic type argument to be non-nullable types. Pour ce faire, ajoutez la contrainte notnull sur cet argument de type.You can do that by adding the notnull constraint on that type argument. Quand cette contrainte est appliquée, l’argument de type ne doit pas être un type Nullable.When that constraint is applied, the type argument must not be a nullable type.

ConclusionsConclusions

L’ajout de types de référence Nullable fournit un vocabulaire initial pour décrire les attentes des API pour les variables qui pourraient être null.Adding nullable reference types provides an initial vocabulary to describe your APIs expectations for variables that could be null. Les attributs supplémentaires fournissent un vocabulaire plus riche pour décrire l’État null des variables en tant que conditions préalables et post-conditions.The additional attributes provide a richer vocabulary to describe the null state of variables as preconditions and postconditions. Ces attributs décrivent plus clairement vos attentes et offrent une meilleure expérience aux développeurs qui utilisent vos API.These attributes more clearly describe your expectations and provide a better experience for the developers using your APIs.

Lorsque vous mettez à jour des bibliothèques pour un contexte Nullable, ajoutez ces attributs pour guider les utilisateurs de vos API vers l’utilisation correcte.As you update libraries for a nullable context, add these attributes to guide users of your APIs to the correct usage. Ces attributs vous aident à décrire complètement l’État null des arguments d’entrée et les valeurs de retour :These attributes help you fully describe the null-state of input arguments and return values:

  • AllowNull a: un argument d’entrée qui n’accepte pas les valeurs NULL peut être null.AllowNull: A non-nullable input argument may be null.
  • DisallowNull: un argument d’entrée Nullable ne doit jamais avoir la valeur null.DisallowNull: A nullable input argument should never be null.
  • MaybeNull: une valeur de retour qui n’accepte pas les valeurs NULL peut être null.MaybeNull: A non-nullable return value may be null.
  • NOTNULL: une valeur de retour Nullable ne sera jamais null.NotNull: A nullable return value will never be null.
  • MaybeNullWhen: un argument d’entrée qui n’accepte pas les valeurs NULL peut être NULL lorsque la méthode retourne la valeur de bool spécifiée.MaybeNullWhen: A non-nullable input argument may be null when the method returns the specified bool value.
  • NotNullWhen: un argument d’entrée Nullable ne sera pas NULL lorsque la méthode retourne la valeur de bool spécifiée.NotNullWhen: A nullable input argument will not be null when the method returns the specified bool value.
  • NotNullIfNotNull: une valeur de retour n’est pas null si l’argument d’entrée pour le paramètre spécifié n’est pas null.NotNullIfNotNull: A return value isn't null if the input argument for the specified parameter isn't null.