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.
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 )MyOptions
à créer avec une valeur name
non nullable car la méthode Configure
construit l'instance d'options pour nouspublic 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?
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
}
}
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.