Wofür ist der falsche Operator in C # gut?

107

Es gibt zwei seltsame Operatoren in C #:

Wenn ich dieses Recht verstehe, können diese Operatoren in Typen verwendet werden, die ich anstelle eines booleschen Ausdrucks verwenden möchte und in denen ich keine implizite Konvertierung in bool bereitstellen möchte.

Angenommen, ich habe eine folgende Klasse:

    public class MyType
    {
        public readonly int Value;

        public MyType(int value)
        {
            Value = value;
        }

        public static bool operator true (MyType mt)
        {
            return  mt.Value > 0;
        }

        public static bool operator false (MyType mt)
        {
            return  mt.Value < 0;
        }

    }

So kann ich folgenden Code schreiben:

    MyType mTrue = new MyType(100);
    MyType mFalse = new MyType(-100);
    MyType mDontKnow = new MyType(0);

    if (mTrue)
    {
         // Do something.
    }

    while (mFalse)
    {
        // Do something else.
    }

    do
    {
        // Another code comes here.
    } while (mDontKnow)

Für alle obigen Beispiele wird jedoch nur der wahre Operator ausgeführt. Wofür ist der falsche Operator in C # gut?

Hinweis: Weitere Beispiele finden Sie hier , hier und hier .

Jakub Šturc
quelle
Moderne Dokumente finden Sie unter docs.microsoft.com/en-us/dotnet/csharp/language-reference/… . Die Dokumente legen nahe, dass es keinen Grund mehr gibt, diesen Operator zu definieren, da C # 2.0 nullfähige Typen hinzugefügt hat.
Mark Amery

Antworten:

65

Sie können es verwenden, um die Operatoren &&und zu überschreiben ||.

Die &&und ||Betreiber können nicht außer Kraft gesetzt werden, aber wenn Sie außer Kraft setzen |, &, trueund falsegenau in der richtigen Art und Weise wird der Compiler aufrufen |und &wenn Sie schreiben ||und &&.

Schauen Sie sich zum Beispiel diesen Code an (von http://ayende.com/blog/1574/nhibernate-criteria-api-operator-overloading - wo ich von diesem Trick erfahren habe ; archivierte Version von @BiggsTRC):

public static AbstractCriterion operator &(AbstractCriterion lhs, AbstractCriterion rhs)
{
       return new AndExpression(lhs, rhs);
}

public static AbstractCriterion operator |(AbstractCriterion lhs, AbstractCriterion rhs)
{
       return new OrExpression(lhs, rhs);
}

public static bool operator false(AbstractCriterion criteria)
{
       return false;
}
public static bool operator true(AbstractCriterion criteria)
{
       return false;
}

Dies ist offensichtlich ein Nebeneffekt und nicht die Art und Weise, wie es verwendet werden soll, aber es ist nützlich.

Nir
quelle
Ihr Link ist 404. Sie sind sich nicht sicher, wie Sie dies bearbeiten möchten, also habe ich es belassen.
user7116
Ich habe die URL mit einer archivierten Kopie aktualisiert, damit sie weiterhin gelesen werden kann.
IAmTimCorey
25

Shog9 und Nir: Danke für deine Antworten. Diese Antworten haben mich auf den Artikel von Steve Eichert und auf msdn verwiesen :

Die Operation x && y wird als T.false (x) ausgewertet. x: T. & (x, y), wobei T.false (x) ein Aufruf des in T falsch deklarierten Operators ist und T. & (x, y) ein Aufruf des ausgewählten Operators & ist. Mit anderen Worten, x wird zuerst ausgewertet und der Operator false wird für das Ergebnis aufgerufen, um zu bestimmen, ob x definitiv falsch ist. Wenn x definitiv falsch ist, ist das Ergebnis der Operation der zuvor für x berechnete Wert. Andernfalls wird y ausgewertet und der ausgewählte Operator & wird für den zuvor für x berechneten Wert und den für y berechneten Wert aufgerufen, um das Ergebnis der Operation zu erzeugen.

Jakub Šturc
quelle
Ich frage mich, warum sie sich nicht dafür entschieden haben, (!T.true(x)) ? x : T.&(x, y)stattdessen die Logik zu verwenden.
Des Nerger
14

Auf der Seite, die Sie mit http://msdn.microsoft.com/en-us/library/6x6y6z4d.aspx verknüpfen, wird angegeben , wofür sie gedacht waren. Dies war eine Möglichkeit, nullfähige Bools zu behandeln, bevor nullfähige Werttypen eingeführt wurden.

Ich würde heutzutage vermuten, dass sie für die gleichen Dinge wie ArrayList gut sind - also absolut nichts.

Will Dean
quelle
7

AFAIK, es würde in einem Test auf falsch verwendet werden, beispielsweise wenn der &&Bediener ins Spiel kommt. Denken Sie daran, && Kurzschlüsse, also im Ausdruck

if ( mFalse && mTrue) 
{
   // ... something
}

mFalse.false()wird aufgerufen, und bei der Rückgabe wird trueder Ausdruck auf einen Aufruf von 'mFalse.true ()' reduziert (der dann zurückkehren sollte, sonst werden die falseDinge seltsam).

Beachten Sie, dass Sie den &Operator implementieren müssen , damit dieser Ausdruck kompiliert werden kann, da er bei mFalse.false()Rückgabe verwendet wird false.

Shog9
quelle
3

Aus dem MSDN-Artikel, den Sie verlinkt haben, geht hervor, dass nullable boolesche Typen zugelassen wurden, bevor der nullable-Typ (dh int?, Bool? Usw.) in die Sprache in C # 2 eingeführt wurde. Sie würden also einen internen Wert speichern, der angibt, ob der Wert wahr oder falsch oder null ist, dh in Ihrem Beispiel> 0 für wahr, <0 für falsch und == 0 für null, und dann würden Sie eine Nullsemantik im SQL-Stil erhalten. Sie müssten auch eine .IsNull-Methode oder -Eigenschaft implementieren, damit die Nichtigkeit explizit überprüft werden kann.

Stellen Sie sich im Vergleich zu SQL eine Tabellentabelle mit 3 Zeilen mit dem Wert Foo auf true, 3 Zeilen mit dem Wert Foo auf false und 3 Zeilen mit dem Wert Foo auf null vor.

SELECT COUNT(*) FROM Table WHERE Foo = TRUE OR Foo = FALSE
6

Um alle Zeilen zu zählen, müssten Sie Folgendes tun: -

SELECT COUNT(*) FROM Table WHERE Foo = TRUE OR Foo = FALSE OR Foo IS NULL
9

Diese 'IS NULL'-Syntax hätte in Ihrer Klasse den gleichen Code wie .IsNull ().

LINQ macht den Vergleich mit C # noch deutlicher: -

int totalCount = (from s in MyTypeEnumerable
                 where s || !s
                 select s).Count();

Stellen Sie sich vor, MyTypeEnumberable hat genau den gleichen Inhalt der Datenbank, dh 3 Werte gleich true, 3 Werte gleich false und 3 Werte gleich null. In diesem Fall würde totalCount in diesem Fall 6 ergeben. Wenn wir den Code jedoch neu geschrieben haben als: -

int totalCount = (from s in MyTypeEnumerable
                 where s || !s || s.IsNull()
                 select s).Count();

Dann würde totalCount 9 ergeben.

Das DBNull-Beispiel im verknüpften MSDN-Artikel zum falschen Operator zeigt eine Klasse in der BCL, die genau dieses Verhalten aufweist.

Tatsächlich ist die Schlussfolgerung, dass Sie dies nicht verwenden sollten, es sei denn, Sie sind sich völlig sicher, dass Sie diese Art von Verhalten wünschen. Es ist besser, nur die weitaus einfachere nullfähige Syntax zu verwenden!

Update: Ich habe gerade bemerkt, dass Sie die Logikoperatoren manuell überschreiben müssen !, || und && damit dies richtig funktioniert. Ich glaube, der falsche Operator speist sich in diese logischen Operatoren ein, dh er zeigt Wahrheit, Falschheit oder "anders" an. Wie in einem anderen Kommentar erwähnt! X funktioniert nicht sofort; du musst überladen! Seltsamkeit!

ljs
quelle