Manière optimale d'utiliser des opérateurs conditionnels nuls dans les expressions booléennes

11

Vous écrivez une expression booléenne qui pourrait ressembler à ceci:

team.Category == "A Team" && team?.Manager?.IsVietnamVet

public class Manager
{
    public bool IsVietnamVet { get; set; }
}

public class Team
{
    public string Category { get; set; }

    public Manager Manager { get; set; }
}

... et vous obtenez une erreur:

L'opérateur '&&' ne peut pas être appliqué aux opérandes de type 'bool' et 'bool?'

Quelle est la manière optimale / la plus propre de le gérer?

  1. team.Category == "A Team" && (team?.Manager?.IsVietnamVet ?? false)

    Est-ce vraiment lisible?

  2. team.Category == "A Team" && (team?.Manager?.IsVietnamVet).GetValueOrDefault()

    Cela peut ne pas fonctionner dans LINQ-to-Entities ...

  3. team.Category == "A Team" && team?.Manager?.IsVietnamVet == true

    Souhaitez-vous vraiment écrire if (condition == true)sans aucune hésitation?

Il y a-t-il des alternatives? Est-il finalement préférable d'écrire:

  1. team.Category == "A Team" && team.Manager != null && team.Manager.IsVietnamVet
Santhos
la source
if (! (String.IsNullOrEmpty (team.Manager) && condition)) ...
Snoop
1
@StevieV c'était ment d'être comme ça dès le début;)
Santhos
1
En regardant le code plus attentivement, la partie conditionnelle nulle devrait être team.Manager?.IsVietnamVet, c'est-à-dire aucune conditionnelle nulle après team, car elle ne peut déjà l'être null.
svick
2
"Pourriez-vous vraiment écrire si (condition == vraie) sans aucune hésitation?" Pour un booléen normal, non, car c'est superflu. Cependant, pour un booléen nullable , cela est pertinent. nullableBool == trueteste essentiellement nullableBool != false && nullableBool != null(et c'est cette deuxième partie qui la rend utile et donc pas superflue)
Flater
2
J'ai commencé à utiliser nullableBool == truesi je ne crée pas de wrapper. Il est lisible car vous n'écrivez normalement pas == truelorsque vous utilisez un booléen ordinaire comme le mentionne @Flater, il suggère donc que la variable est nullable. De plus, cela améliore la lisibilité de LINQ car vous n'utilisez pas plusieurs conditions nulles comme le mentionne @Fabio.
Santhos

Réponses:

4

Dans ce cas particulier, il pourrait être judicieux de suivre la loi de Déméter, c'est-à-dire

public class Team
{
    public bool IsManagerVietnamVet => Manager?.IsVietnamVet ?? false;
}    

Plus généralement, si une expression booléenne est complexe ou moche, il n'y a rien à dire que vous ne pouvez pas la diviser (ou une partie) en une instruction distincte:

bool isVietnamVet = Manager?.IsVietnamVet ?? false;

if (team.Category == "A Team" && isVietnamVet)

Lorsque vous parcourez le code dans le débogueur, il est souvent plus agréable d'avoir des conditions élaborées empaquetées en une seule booljuste pour économiser un peu de survol de la souris; en fait, il serait peut-être plus agréable de mettre le tout dans une boolvariable.

bool isVietnamVetAndCategoryA = (team.Category == "A Team"
    && Manager?.IsVietnamVet ?? false);

if (isVietnamVetAndCategoryA)

ou avec LINQ:

var wibble = from flight in airport
             from passenger in flight.manifest
             let isOnPlane = 
                 (flight.FinishedBoarding && passenger.Flight == flight.FlightNumber)
             where !isOnPlane
             select passenger;
Ben Cottrell
la source
Je pense que je penche principalement vers une telle solution.
Santhos
1
Nouvelle syntaxe C # pour enregistrer quelques lignes: public bool IsManagerVietnamVet => (team.Category == "") && (team.Manager? .IsVietnamVet ?? false);
Graham
Gardez juste à l'esprit que les longs a && b && c &&...sont exécutés séquentiellement, et le suivant n'est même pas exécuté si le précédent l'est false. Donc, les gens mettent généralement le plus vite au début. Gardez cela à l'esprit lorsque vous déplacez des choses dans un bool-var
séparé
@jitbit Il s'agit davantage de maintenabilité. Les micro-optimisations telles que celle que vous mentionnez ne sont généralement pas nécessaires - cela ne vaut pas la peine de s'inquiéter à moins que les performances ne soient identifiées comme un problème et que le profilage montre que faire de telles modifications aurait un impact notable.
Ben Cottrell
@BenCottrell Je dirais que c'est une "micro" optimisation si la IsManagerVietnamVetpropriété va vérifier dans une base de données;)
jitbit
3

Je pense que l' option 3 (c. -à- == true) est la plus propre moyen de test un bool?est true, parce qu'il est très clair sur ce qu'il fait.

Dans la plupart des codes, cela x == truen'a pas de sens, car c'est la même chose que x, mais cela ne s'applique pas ici, donc je pense que == truece ne serait pas très déroutant.

svick
la source
1
Je pense que ce serait certainement la voie à suivre si nous voulions utiliser l'opérateur conditionnel nul, car il est également sûr pour linq. La question est de savoir si la lisibilité est bonne. De mon point de vue, le choix est entre opt 3 et opt ​​4 et je choisirais 4 sur 3 pour la lisibilité, l'intention du programmeur semble également un peu plus claire. J'ai marqué la réponse de Ben Cottrell comme correcte parce que j'aime un peu plus cette approche. Si je pouvais marquer deux réponses, je le ferais.
Santhos
1
@Santhos - re "l'intention du programmeur semble un peu plus claire". À mon humble avis, c'est un cas où la première fois qu'un programmeur voit a?.b == truequ'il sera confus, mais une fois qu'il comprend ce qu'il fait, cela devient un idiome très lisible, bien plus agréable que de devoir tester != nullau milieu d'une expression complexe. Même chose pour a?.b ?? false, qui semble avoir gagné en popularité comme solution la plus populaire, car elle correspond à ce que vous saisiriez si vous aviez affaire à un type autre que booléen [bien que je n'aime pas cela pour booléen; pour moi, ça ne se lit pas aussi naturellement; Je préfère == true].
ToolmakerSteve
3

S'étendant sur la réponse de Ben Cottrell, le motif "Null Object" peut vous aider davantage.

Au lieu de renvoyer une nulléquipe / un gestionnaire, extrayez ITeamet IManagerinterfacez et renvoyez des implémentations alternatives significatives:

public class NoManager : IManager
{
    public bool IsVietnamVet => false;
}

public class NoTeam : ITeam
{
    public bool ManagedByVietnamVet => false;

    public IManager Manager => new NoManager();
}

Ensuite, tout d'un coup, vous pouvez le faire en team.ManagedByVietnamVettoute sécurité.

Bien sûr, cela dépend du fournisseur en amont de teampour être null-safe - mais cela peut être assuré avec des tests appropriés.

Ant P
la source
-4

J'ai écrit une classe simple que vous pourriez utiliser:

 public class MyBool 
    {
        public bool? Value { get; set; }

        public MyBool(bool b)
        {
            Value = b;
        }

        public MyBool(bool? b)
        {
            Value = b;
        }

        public static implicit operator bool(MyBool m)
        {
            return m?.Value ?? false;
        }

        public static implicit operator bool?(MyBool m)
        {
            return m?.Value;
        }

        public static implicit operator MyBool(bool m)
        {
            return new MyBool(m);
        }

        public static implicit operator MyBool(bool? m)
        {
            return new MyBool(m);
        }

        public override string ToString()
        {
            return Value.ToString();
        }
    }

S'il est fiable d'utiliser un type personnalisé, bien sûr. Vous pouvez comparer MyBoolavec les deux boolet Nullable<bool>.

Logerfo
la source
1
Ce site est destiné aux questions sur la conception de logiciels plutôt que sur la façon de faire fonctionner des morceaux de code spécifiques, donc si vous pensez que c'est la meilleure solution, votre réponse devrait se concentrer sur les raisons pour lesquelles vous pensez qu'une classe comme celle-ci est la meilleure solution possible, pas la le code exact nécessaire pour l'implémenter.
Ixrec