Optimale Möglichkeit, bedingte Nulloperatoren in booleschen Ausdrücken zu verwenden

11

Sie schreiben einen booleschen Ausdruck, der folgendermaßen aussehen könnte:

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; }
}

... und Sie erhalten eine Fehlermeldung:

Der Operator '&&' kann nicht auf Operanden vom Typ 'bool' und 'bool?'

Was ist der optimale / sauberste Weg, damit umzugehen?

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

    Ist das wirklich lesbar?

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

    In LINQ-to-Entities funktioniert dies möglicherweise nicht ...

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

    Würden Sie wirklich if (condition == true)ohne zu zögern schreiben ?

Gibt es noch andere Möglichkeiten? Ist es letztendlich besser zu schreiben:

  1. team.Category == "A Team" && team.Manager != null && team.Manager.IsVietnamVet
Santhos
quelle
if (! (String.IsNullOrEmpty (team.Manager) && Bedingung)) ...
Snoop
1
@StevieV es war von Anfang an so zu sein;)
Santhos
1
Bei genauerer Betrachtung des Codes sollte der nullbedingte Teil sein team.Manager?.IsVietnamVet, dh keine nullbedingte Folge team, da dies bereits nicht möglich ist null.
Svick
2
"Würden Sie wirklich schreiben, wenn (Bedingung == wahr) ohne zu zögern?" Für einen normalen Booleschen, nein, da er überflüssig ist. Für einen nullbaren Booleschen Wert ist dies jedoch relevant. nullableBool == truetestet im Grunde genommen nullableBool != false && nullableBool != null(und es ist dieser zweite Teil, der es nützlich und daher nicht überflüssig macht)
Flater
2
Ich habe angefangen zu verwenden, nullableBool == truewenn ich keinen Wrapper erstelle. Es ist lesbar, da Sie normalerweise nicht schreiben, == truewenn Sie einen regulären Booleschen Wert verwenden, wie @Flater erwähnt. Daher wird vorgeschlagen, dass die Variable nullwertfähig ist. Darüber hinaus wird die LINQ-Lesbarkeit verbessert, da Sie nicht mehrere Nullbedingungen verwenden, wie @Fabio erwähnt.
Santhos

Antworten:

4

In diesem speziellen Fall kann es ratsam sein, das Gesetz von Demeter zu befolgen, d. H.

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

Wenn ein boolescher Ausdruck komplex oder hässlich ist, gibt es nichts zu sagen, dass Sie ihn (oder einen Teil davon) nicht in eine separate Anweisung aufteilen können:

bool isVietnamVet = Manager?.IsVietnamVet ?? false;

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

Wenn Sie den Code im Debugger durchgehen, ist es oft besser, ausgefeilte Bedingungen in einer einzigen boolzu packen, um ein wenig Mouseover-Hovering zu vermeiden. In der Tat könnte es einfacher sein, das Ganze in eine boolVariable zu setzen.

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

if (isVietnamVetAndCategoryA)

oder mit 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
quelle
Ich glaube, ich neige hauptsächlich zu einer solchen Lösung.
Santhos
1
Neue C # -Syntax zum Speichern einiger Zeilen: public bool IsManagerVietnamVet => (team.Category == "") && (team.Manager? .IsVietnamVet ?? false);
Graham
Denken Sie daran, dass lange a && b && c &&...nacheinander ausgeführt werden und die nächste nicht einmal ausgeführt wird, wenn die vorherige ausgeführt wird false. Die Leute setzen also normalerweise am Anfang am schnellsten. Denken Sie daran, wenn Sie Sachen in eine separate Bool-Var
Jitbit
@jitbit Es geht mehr um Wartbarkeit. Mikrooptimierungen wie die von Ihnen erwähnte sind im Allgemeinen nicht erforderlich - es lohnt sich nicht, sich Sorgen zu machen, es sei denn, die Leistung wird als Problem identifiziert, und die Profilerstellung zeigt, dass solche Änderungen spürbare Auswirkungen haben würden.
Ben Cottrell
@ BenCottrell Ich würde es eine "Mikro" -Optimierung nennen, wenn die IsManagerVietnamVetEigenschaft in einer Datenbank
eingecheckt wird
3

Ich denke, Option 3 (dh == true) ist der sauberste Weg, um zu testen, ob a bool?ist true, weil es sehr klar ist , was es tut.

In den meisten Fällen x == truemacht Code keinen Sinn, weil es das gleiche ist wie x, aber das gilt hier nicht, also denke ich, == truewäre nicht sehr verwirrend.

svick
quelle
1
Ich denke, dies wäre definitiv der richtige Weg, wenn wir den Null-Bedingungsoperator verwenden möchten, da er auch linq-sicher ist. Die Frage ist, ob die Lesbarkeit gut ist. Aus meiner Sicht ist die Wahl zwischen opt 3 und opt 4 und ich würde aus Gründen der Lesbarkeit 4 über 3 wählen, auch die Absicht des Programmierers scheint etwas klarer zu sein. Ich habe Ben Cottrells Antwort als richtig markiert, weil mir dieser Ansatz etwas besser gefällt. Wenn ich zwei Antworten markieren könnte, würde ich.
Santhos
1
@Santhos - re "Die Absicht des Programmierers scheint etwas klarer zu sein". IMHO, dies ist ein Fall, in dem ein Programmierer das erste Mal sieht a?.b == true, dass er verwirrt ist, aber sobald er versteht, was er tut, wird es zu einer sehr lesbaren Redewendung, viel schöner, als != nullmitten in einem komplexen Ausdruck testen zu müssen . Gleiches gilt für a?.b ?? false, was als beliebteste Lösung an Bedeutung gewonnen zu haben scheint, da es mit dem übereinstimmt, was Sie eingeben würden, wenn Sie mit einem anderen Typ als Boolean arbeiten würden [obwohl ich es für Boolean nicht mag; für mich liest es sich nicht so natürlich; Ich bevorzuge == true].
ToolmakerSteve
3

Wenn Sie die Antwort von Ben Cottrell erweitern, kann Ihnen das Muster "Null Object" weiterhelfen.

Anstatt ein nullTeam / einen Manager zurückzugeben, extrahieren ITeamund IManagerverbinden Sie und geben Sie sinnvolle alternative Implementierungen zurück:

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

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

    public IManager Manager => new NoManager();
}

Dann können Sie plötzlich team.ManagedByVietnamVetsicher tun .

Dies setzt natürlich voraus, dass der Upstream-Anbieter teamnull-sicher ist - dies kann jedoch durch entsprechende Tests sichergestellt werden.

Ameise P.
quelle
-4

Ich habe eine einfache Klasse geschrieben, die Sie verwenden können:

 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();
        }
    }

Wenn es zuverlässig ist, einen benutzerdefinierten Typ zu verwenden, natürlich. Sie können MyBoolmit beiden boolund vergleichen Nullable<bool>.

Logerfo
quelle
1
Auf dieser Website geht es eher um Fragen zum Software-Design als darum, wie bestimmte Codeteile zum Laufen gebracht werden können. Wenn Sie also der Meinung sind, dass dies die beste Lösung ist, sollte sich Ihre Antwort darauf konzentrieren, warum Sie glauben, dass eine Klasse wie diese die bestmögliche Lösung ist, nicht die genauer Code benötigt, um es zu implementieren.
Ixrec