web-dev-qa-db-fra.com

Types de référence nullables et modèle d'options

Comment pouvons-nous utiliser les types de référence non nullables en combinaison avec le modèle d'options ?

Disons que nous avons un modèle d'options nommé MyOptions.

Les services nécessitant ces options obtiennent IOptions<MyOptions> options injecté dans le constructeur.

La configuration des options se produit sur le IServiceCollection comme ceci:

services
    .AddOptions<MyOptions>()
    .Configure(options =>
    {
        options.Name = "ABC";
    });

Maintenant, le problème est dans la définition de MyOptions:

public sealed class MyOptions
{
    public string Name { get; set; }
}

ce qui génère l'avertissement:

CS8618 La propriété non nullable 'Nom' n'est pas initialisée. Envisagez de déclarer la propriété comme nullable.

  1. Nous ne voulons pas rendre Name nullable car nous devons alors placer des vérifications null traditionnelles partout (ce qui va à l'encontre des types de référence non nullables )
  2. Nous ne pouvons pas créer de constructeur pour appliquer la classe MyOptions à créer avec une valeur name non nullable car la méthode Configure construit l'instance d'options pour nous
  3. Nous ne pouvons pas utiliser l’opérateur pardon nul (public string name { get; set; } = null!;) car alors nous ne pouvons pas nous assurer que la propriété Name est définie et nous pouvons nous retrouver avec un null dans la propriété Name où cela ne serait pas prévu (à l'intérieur les services)

Une autre option que j'ai oublié de considérer?

16
huysentruitw

Si le comportement attendu de la propriété est qu'elle peut initialement contenir null mais ne doit jamais être définie sur null, essayez d'utiliser DisallowNullAttribute .

#nullable enable

using System.Diagnostics.CodeAnalysis;

public sealed class MyOptions
{
    [DisallowNull]
    public string? Name { get; set; }

    public static void Test()
    {
        var options = new MyOptions();
        options.Name = null; // warning
        options.Name = "Hello"; // ok
    }

    public static void Test2()
    {
        var options = new MyOptions();
        options.Name.Substring(1); // warning on dereference
    }
}
3
Rikki Gibson

Il semble que vous ayez deux options possibles ici. La première consiste à initialiser une propriété Options en utilisant une chaîne vide (au lieu de la valeur null) pour éviter les vérifications null

public sealed class MyOptions
{
    public string Name { get; set; } = "";
}

La seconde consiste à rendre toutes les propriétés nulles et à les décorer en utilisant DisallowNull précondition et NotNull postcondition.

DisallowNull signifie que l'argument d'entrée nullable ne doit jamais être null, NotNull - une valeur de retour nullable ne sera jamais null. Mais ces attributs n'affectent que l'analyse annulable pour les appelants des membres qui sont annotés avec eux. Donc, vous indiquez que votre propriété ne peut jamais retourner null ou être définie sur null, malgré une déclaration nullable

public sealed class MyOptions
{
    [NotNull, DisallowNull]public string? Name { get; set; }
}

et l'exemple d'utilisation

var options = new MyOptions();
options.Name = null; //warning CS8625: Cannot convert null literal to non-nullable reference type.
options.Name = "test";

mais l'exemple suivant n'affiche pas d'avertissement, car l'analyse nullable ne fonctionne pas encore correctement dans les initialiseurs d'objet, voyez ceci problème GitHub

var options = new MyOptions { Name = null }; //no warning

La même image pour le getter de propriété, l'exemple suivant n'affiche aucun avertissement, car vous avez indiqué que le type de retour nullable ne peut pas être null

var options = new MyOptions();
string test = options.Name.ToLower();

mais tenter de définir une valeur null et de l'obtenir génère un avertissement (le compilateur est suffisamment intelligent pour détecter de tels scénarios)

var options = new MyOptions() { Name = null };
string test = options.Name.ToLower(); //warning CS8602: Dereference of a possibly null reference.
3
Pavel Anikhouski