Comment gérer la «propriété non nullable 'S2' doit contenir une valeur non nulle» - avertissement pour le constructeur privé dans l'exemple suivant:

 #nullable enable
 class X {...}
 class Y {...}
 class Z {...}     
 class C {
   public string S1 { get; }
   public string S2 { get; }
   private C(Z z) => S1 = GenerateS1FromZ(z);
   public C(Z z, X x) : this(z) => S2 = GenerateS2FromX(x);
   public C(Z z, Y y) : this(z) => S2 = GenerateS2FromY(y);

   private static string GenerateS1FromZ(Z z) { ... }
   private static string GenerateS2FromX(X x) { ... }
   private static string GenerateS2FromY(Y y) { ... }
 }

S1 doit être initialisé dans un constructeur, car il n'a pas de setter. S2 peut être initialisé de deux manières, il y a donc deux constructeurs publics.

Supposons que S1 et S2 ne sont que des espaces réservés pour quelques propriétés supplémentaires. Par conséquent, il n'est pas pratique de passer la valeur de S2 au constructeur privé en tant que paramètre ou d'initialiser S1 dans les deux constructeurs publics.

Sous cette hypothèse, le code ci-dessus est ok je pense, mais l'avertissement non nullable ne fonctionne pas comme je le souhaite. On pourrait ajouter S2=null!; dans le constructeur privé pour éviter l'avertissement, mais cela semble être un hack.

c#
2
MarkusParker 23 févr. 2021 à 13:11

2 réponses

Meilleure réponse

Se référant à votre besoin

Supposons que S1 et S2 ne sont que des espaces réservés pour quelques propriétés supplémentaires. Par conséquent, il n'est pas pratique de transmettre la valeur de S2 au constructeur privé en tant que paramètre ou d'initialiser S1 dans les deux constructeurs publics.

C'est un bon conseil pour refactoriser et extraire ces propriétés pour séparer les classes ou les structures. Si vous le faites, vous pouvez ensuite transmettre les valeurs à votre constructeur privé de C et supprimer l'avertissement non nullable.

Voici un exemple simple d'exhaustivité

class S1
{
    public string S1Prop { get; }

    public S1(Z z) => S1Prop = GenerateFrom(z);
    private static string GenerateFrom(Z z) { /* ... */ return "From z"; }
}

class S2
{
    public string S2Prop { get; }

    public S2(X x) => S2Prop = GenerateFrom(x);
    public S2(Y y) => S2Prop = GenerateFrom(y);
    private static string GenerateFrom(X x) { /* ... */ return "From x"; }
    private static string GenerateFrom(Y y) { /* ... */ return "From y"; }
}

class C
{
    public S1 S1 { get; }
    public S2 S2 { get; }

    private C(S1 s1, S2 s2) { S1 = s1; S2 = s2; }

    public C(Z z, X x) : this(new S1(z), new S2(x)) { }
    public C(Z z, Y y) : this(new S1(z), new S2(y)) { }
}

En fonction de vos besoins, vous pouvez également supprimer la dépendance sur les classes X, Y et Z en rendant le constructeur privé public et en supprimant l'autre deux.

class C2 // No dependency to X, Y, Z
{
    public S1 S1 { get; }
    public S2 S2 { get; }

    public C2(S1 s1, S2 s2) { S1 = s1; S2 = s2; }
}
1
G Wimpassinger 8 mars 2021 à 20:51

Bien que l'assignation de S2 à null! puisse empêcher le compilateur de se plaindre, cela rend le code plus fragile à d'autres modifications car quelqu'un d'autre pourrait ajouter un nouveau constructeur sans initialiser S2.

À mon avis, la meilleure solution est de créer un constructeur qui attribuera S2 et l'appellera partout.

#nullable enable
    class X { }
    class Y { }
    class Z { }
    class C
    {
        public string S1 { get; }
        public string S2 { get; }
        private C(Z z, string s2)
        {
            S1 = GenerateS1FromZ(z);
            S2 = s2; // assign here
        }

        public C(Z z, X x) : this(z, GenerateS2FromX(x)) { } // remove assignment here
        public C(Z z, Y y) : this(z, GenerateS2FromY(y)) { }

        private static string GenerateS1FromZ(Z z) => "";
        private static string GenerateS2FromX(X x) => "";
        private static string GenerateS2FromY(Y y) => "";
    }
0
JL0PD 23 févr. 2021 à 12:00