Warum müssen wir sowohl == als auch! = In C # definieren?


346

Der C # -Compiler verlangt, dass ein benutzerdefinierter Typ, der den Operator definiert ==, auch definiert werden muss !=(siehe hier ).

Warum?

Ich bin gespannt, warum die Designer dies für notwendig hielten und warum der Compiler nicht für einen der Operatoren eine vernünftige Implementierung verwenden kann, wenn nur der andere vorhanden ist. Mit Lua können Sie beispielsweise nur den Gleichheitsoperator definieren und den anderen kostenlos erhalten. C # könnte dasselbe tun, indem Sie aufgefordert werden, entweder == oder beide == und! = Zu definieren und dann den fehlenden Operator! = Automatisch als zu kompilieren !(left == right).

Ich verstehe, dass es seltsame Eckfälle gibt, in denen einige Entitäten möglicherweise weder gleich noch ungleich sind (wie IEEE-754 NaNs), aber diese scheinen die Ausnahme und nicht die Regel zu sein. Dies erklärt also nicht, warum die C # -Compiler-Designer die Ausnahme zur Regel gemacht haben.

Ich habe Fälle von schlechter Verarbeitung gesehen, in denen der Gleichheitsoperator definiert ist, dann ist der Ungleichheitsoperator ein Copy-Paste, bei dem jeder Vergleich umgekehrt und jedes && auf ein || umgeschaltet wird (Sie verstehen ... im Grunde! (a == b) erweitert durch De Morgans Regeln). Das ist eine schlechte Praxis, die der Compiler durch Design beseitigen könnte, wie es bei Lua der Fall ist.

Hinweis: Gleiches gilt für Operatoren <> <=> =. Ich kann mir keine Fälle vorstellen, in denen Sie diese auf unnatürliche Weise definieren müssen. Mit Lua können Sie nur <und <= und> = und> natürlich durch die Negation der Formers definieren. Warum macht C # nicht dasselbe (zumindest "standardmäßig")?

BEARBEITEN

Anscheinend gibt es triftige Gründe, dem Programmierer zu erlauben, Überprüfungen auf Gleichheit und Ungleichheit durchzuführen, wie sie möchten. Einige der Antworten weisen auf Fälle hin, in denen dies hilfreich sein kann.

Der Kern meiner Frage ist jedoch, warum dies in C # zwangsweise erforderlich ist, wenn dies normalerweise nicht logisch erforderlich ist.

Es steht auch in auffallendem Kontrast zu Entwurfsentscheidungen für .NET-Schnittstellen wie Object.Equals, bei IEquatable.Equals IEqualityComparer.Equalsdenen das Fehlen eines NotEqualsGegenstücks zeigt, dass das Framework !Equals()Objekte als ungleich betrachtet, und das ist es. Darüber hinaus hängen Klassen wie Dictionaryund Methoden wie .Contains()ausschließlich von den oben genannten Schnittstellen ab und verwenden die Operatoren nicht direkt, selbst wenn sie definiert sind. Wenn ReSharper Gleichheitsmitglieder generiert, definiert es sowohl ==als auch !=in Bezug auf Equals()und selbst dann nur dann, wenn der Benutzer überhaupt Operatoren generiert. Die Gleichheitsoperatoren werden vom Framework nicht benötigt, um die Objektgleichheit zu verstehen.

Grundsätzlich kümmert sich das .NET Framework nicht um diese Operatoren, sondern nur um einige EqualsMethoden. Die Entscheidung, dass sowohl == als auch! = Operatoren vom Benutzer gemeinsam definiert werden müssen, bezieht sich ausschließlich auf das Sprachdesign und nicht auf die Objektsemantik in Bezug auf .NET.


24
+1 für eine ausgezeichnete Frage. Es scheint überhaupt keinen Grund zu geben ... natürlich könnte der Compiler annehmen, dass a! = B in! (A == b) übersetzt werden kann, sofern nicht anders angegeben. Ich bin gespannt, warum sie sich dazu entschlossen haben.
Patrick87

16
Dies ist die schnellste Frage, die ich je gesehen habe.
Christopher Currens

26
Ich bin sicher, dass @Eric Lippert auch eine fundierte Antwort geben kann.
Yuck

17
"Der Wissenschaftler ist keine Person, die die richtigen Antworten gibt, er ist eine Person, die die richtigen Fragen stellt." Aus diesem Grund werden in SE Fragen angeregt. Und es funktioniert!
Adriano Carneiro

4
Die gleiche Frage für Python: stackoverflow.com/questions/4969629/…
Josh Lee

Antworten:


161

Ich kann nicht für die Sprachdesigner sprechen, aber nach allem, was ich begründen kann, scheint es eine absichtliche, richtige Designentscheidung zu sein.

Wenn Sie sich diesen grundlegenden F # -Code ansehen, können Sie diesen in eine funktionierende Bibliothek kompilieren. Dies ist ein gesetzlicher Code für F # und überlastet nur den Gleichheitsoperator, nicht die Ungleichung:

module Module1

type Foo() =
    let mutable myInternalValue = 0
    member this.Prop
        with get () = myInternalValue
        and set (value) = myInternalValue <- value

    static member op_Equality (left : Foo, right : Foo) = left.Prop = right.Prop
    //static member op_Inequality (left : Foo, right : Foo) = left.Prop <> right.Prop

Das macht genau das, wie es aussieht. Es wird nur ein Gleichheitsvergleich erstellt ==und überprüft, ob die internen Werte der Klasse gleich sind.

Während Sie nicht eine Klasse wie dies in C # erstellen, Sie können eine , die für .NET kompiliert wurde. Es ist offensichtlich, dass unser überladener Operator für verwendet == wird. Wofür wird die Laufzeit verwendet !=?

Der C # EMCA-Standard enthält eine ganze Reihe von Regeln (Abschnitt 14.9), in denen erläutert wird, wie bestimmt wird, welcher Operator bei der Bewertung der Gleichheit verwendet werden soll. Um es zu vereinfachen und damit nicht ganz genau auszudrücken: Wenn die zu vergleichenden Typen vom gleichen Typ sind und ein überladener Gleichheitsoperator vorhanden ist, wird diese Überladung verwendet und nicht der von Object geerbte Standardreferenzgleichheitsoperator. Es ist daher nicht verwunderlich, dass, wenn nur einer der Operatoren vorhanden ist, der Standard-Referenzgleichheitsoperator verwendet wird, den alle Objekte haben, für den es keine Überladung gibt. 1

In dem Wissen, dass dies der Fall ist, lautet die eigentliche Frage: Warum wurde dies so entworfen und warum findet der Compiler es nicht selbst heraus? Viele Leute sagen, dass dies keine Entwurfsentscheidung war, aber ich denke gerne, dass dies so gedacht wurde, insbesondere in Bezug auf die Tatsache, dass alle Objekte einen Standardgleichheitsoperator haben.

Warum erstellt der Compiler den !=Operator nicht automatisch ? Ich kann es nicht genau wissen, es sei denn, jemand von Microsoft bestätigt dies, aber das kann ich anhand der Argumentation zu den Fakten feststellen.


Um unerwartetes Verhalten zu verhindern

Vielleicht möchte ich einen Wertevergleich durchführen ==, um die Gleichheit zu testen. Wenn es darum ging, war es !=mir überhaupt egal, ob die Werte gleich waren, es sei denn, die Referenz war gleich, denn damit mein Programm sie als gleich betrachtet, ist es mir nur wichtig, ob die Referenzen übereinstimmen. Schließlich wird dies tatsächlich als Standardverhalten des C # beschrieben (wenn beide Operatoren nicht überladen wären, wie dies bei einigen in einer anderen Sprache geschriebenen .net-Bibliotheken der Fall wäre). Wenn der Compiler automatisch Code hinzufügte, konnte ich mich nicht mehr darauf verlassen, dass der Compiler Code ausgibt, der kompatibel sein sollte. Der Compiler sollte keinen versteckten Code schreiben, der Ihr Verhalten ändert, insbesondere wenn der von Ihnen geschriebene Code den Standards von C # und der CLI entspricht.

In Bezug darauf , dass Sie gezwungen sind, es zu überlasten, anstatt zum Standardverhalten überzugehen, kann ich nur fest sagen, dass es im Standard (EMCA-334 17.9.2) 2 enthalten ist . Der Standard gibt nicht an, warum. Ich glaube, das liegt an der Tatsache, dass C # viel Verhalten von C ++ übernimmt. Weitere Informationen hierzu finden Sie weiter unten.


Wenn Sie !=und überschreiben ==, müssen Sie bool nicht zurückgeben.

Dies ist ein weiterer wahrscheinlicher Grund. In C # ist diese Funktion:

public static int operator ==(MyClass a, MyClass b) { return 0; }

ist so gültig wie dieser:

public static bool operator ==(MyClass a, MyClass b) { return true; }

Wenn Sie etwas anderes als bool zurückgeben, kann der Compiler nicht automatisch auf einen entgegengesetzten Typ schließen. Ferner wird in dem Fall , in dem Ihr Bediener tut Rückkehr Bool, es macht einfach keinen Sinn machen für sie Code erzeugen generieren , die nur in diesem einen speziellen Fall existieren würde oder, wie ich oben sagte, Code, versteckt das Standardverhalten der CLR.


C # leiht sich viel von C ++ 3 aus

Als C # eingeführt wurde, gab es einen Artikel im MSDN-Magazin, der über C # schrieb:

Viele Entwickler wünschen sich eine Sprache, die wie Visual Basic einfach zu schreiben, zu lesen und zu warten ist, aber dennoch die Leistungsfähigkeit und Flexibilität von C ++ bietet.

Ja, das Entwurfsziel für C # bestand darin, fast die gleiche Leistung wie C ++ bereitzustellen und nur wenig für Annehmlichkeiten wie starre Typensicherheit und Speicherbereinigung zu opfern. C # wurde stark nach C ++ modelliert.

Sie werden nicht überrascht sein zu erfahren, dass in C ++ die Gleichheitsoperatoren kein bool zurückgeben müssen , wie in diesem Beispielprogramm gezeigt

Nun, C ++ nicht direkt benötigen Sie den komplementären Betreiber zu überlasten. Wenn Sie den Code im Beispielprogramm kompiliert haben, wird er fehlerfrei ausgeführt. Wenn Sie jedoch versucht haben, die Zeile hinzuzufügen:

cout << (a != b);

Sie erhalten

Compilerfehler C2678 (MSVC): binär '! =': Es wurde kein Operator gefunden, der einen linken Operanden vom Typ 'Test' verwendet (oder es gibt keine akzeptable Konvertierung).

Während C ++ selbst nicht erfordert, dass Sie paarweise überladen, wird dies nicht der Fall sein Gleichheitsoperator verwenden, den Sie für eine benutzerdefinierte Klasse nicht überladen haben. Es ist in .NET gültig, da alle Objekte ein Standardobjekt haben. C ++ nicht.


1. Nebenbei bemerkt, der C # -Standard verlangt weiterhin, dass Sie das Operatorpaar überladen, wenn Sie einen der Operatoren überladen möchten. Dies ist ein Teil des Standards und nicht nur der Compiler . Es gelten jedoch dieselben Regeln für die Bestimmung, welcher Operator aufgerufen werden soll, wenn Sie auf eine .net-Bibliothek zugreifen, die in einer anderen Sprache geschrieben ist, für die nicht dieselben Anforderungen gelten.

2. EMCA-334 (pdf) ( http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf )

3. Und Java, aber darum geht es hier wirklich nicht


75
" Sie müssen nicht bool zurückgeben " .. Ich denke, das ist unsere Antwort genau dort; gut gemacht. Wenn! (O1 == o2) nicht einmal genau definiert ist, können Sie nicht erwarten, dass sich der Standard darauf verlässt.
Brian Gordon

10
Ironischerweise haben Sie in einem Thema über logische Operatoren im Satz ein Doppel-Negativ verwendet: "Mit C # können Sie nicht nur einen von beiden überschreiben", was logischerweise bedeutet, dass Sie mit C # nur einen von beiden überschreiben können.
Dan Diplo

13
@ Dan Diplo, es ist okay, es ist nicht so, dass es mir nicht verboten ist, das nicht zu tun oder nicht.
Christopher Currens

6
Wenn das stimmt, vermute ich, dass # 2 richtig ist; aber jetzt stellt sich die frage, warum ==man etwas anderes als a zurückgeben darf bool? Wenn Sie ein zurückgeben int, können Sie es nicht mehr als bedingte Anweisung verwenden, z. if(a == b)wird nicht mehr kompiliert. Was ist der Punkt?
BlueRaja - Danny Pflughoeft

2
Sie könnten ein Objekt zurückgeben, das eine implizite Konvertierung in bool hat (Operator true / false), aber ich sehe immer noch nicht, wo dies nützlich wäre.
Stefan Dragnev

53

Wahrscheinlich, wenn jemand eine dreiwertige Logik implementieren muss (dh null). In solchen Fällen - zum Beispiel ANSI-Standard-SQL - können die Operatoren je nach Eingabe nicht einfach negiert werden.

Sie könnten einen Fall haben, in dem:

var a = SomeObject();

Und a == truekehrt falseund a == falseauch zurückkehrt false.


1
Ich würde in diesem Fall sagen, da aes kein Boolescher Wert ist, sollten Sie ihn mit einer Aufzählung mit drei Zuständen vergleichen, nicht mit einer realen trueoder false.
Brian Gordon

18
Übrigens kann ich nicht glauben, dass niemand auf den FileNotFound- Witz verwiesen hat .
Brian Gordon

28
Wenn a == trueist , falsedann würde ich immer erwarten a != truezu sein true, obwohl a == falseseinfalse
Fede

20
@Fede traf den Nagel auf den Kopf. Dies hat wirklich nichts mit der Frage zu tun - Sie erklären, warum jemand möglicherweise überschreiben möchte, ==und nicht, warum er gezwungen werden sollte, beide zu überschreiben.
BlueRaja - Danny Pflughoeft

6
@Yuck - Ja, es gibt eine gute Standardimplementierung: Dies ist der übliche Fall. Selbst in Ihrem Beispiel wäre die Standardimplementierung von !=korrekt. In dem äußerst ungewöhnlichen Fall, in dem wir wollen x == yund x != ybeide falsch sind (ich kann mir kein einziges sinnvolles Beispiel vorstellen) , könnten sie die Standardimplementierung dennoch überschreiben und ihre eigene bereitstellen. Machen Sie immer den allgemeinen Fall zum Standard!
BlueRaja - Danny Pflughoeft

25

Abgesehen davon, dass C # in vielen Bereichen auf C ++ verzichtet, ist die beste Erklärung, die ich mir vorstellen kann, dass Sie in einigen Fällen eine leichte Abweichung wünschen anderen Ansatz zum Nachweis von "Nicht-Gleichheit" als zum Nachweis von "Gleichheit" .

Offensichtlich können Sie beim Vergleich von Zeichenfolgen beispielsweise nur auf Gleichheit und returnaußerhalb der Schleife testen, wenn Sie nicht übereinstimmende Zeichen sehen. Bei komplizierteren Problemen ist es jedoch möglicherweise nicht so sauber. Der Blütenfilter fällt mir ein; es ist sehr einfach zu schnell sagen , wenn das Element nicht in der Menge, aber schwer zu sagen , ob das Element ist in dem Satz. Obwohl die gleiche returnTechnik angewendet werden könnte, ist der Code möglicherweise nicht so hübsch.


6
Tolles Beispiel; Ich denke du bist auf dem Grund warum. Eine einfache !Sperren Sie in eine einheitliche Definition für die Gleichstellung, wo eine informierte Codierer der Lage sein könnte zu optimieren beide == und !=.
Yuck

13
Dies erklärt also, warum sie die Option erhalten sollten , beide zu überschreiben, und nicht, warum sie dazu gezwungen werden sollten .
BlueRaja - Danny Pflughoeft

1
In diesem Sinne müssen sie nicht beide optimieren , sondern nur eine klare Definition von beiden liefern .
Jesper

22

Wenn Sie sich Implementierungen von Überladungen von == und! = In der .net-Quelle ansehen, implementieren sie häufig nicht! = As! (Links == rechts). Sie implementieren es vollständig (wie ==) mit negierter Logik. Beispielsweise implementiert DateTime == as

return d1.InternalTicks == d2.InternalTicks;

und! = as

return d1.InternalTicks != d2.InternalTicks;

Wenn Sie (oder der Compiler, wenn er es implizit getan hat)! = As implementieren würden

return !(d1==d2);

dann nehmen Sie eine Annahme über die interne Implementierung von == und! = in den Dingen an, auf die Ihre Klasse verweist. Diese Annahme zu vermeiden, könnte die Philosophie hinter ihrer Entscheidung sein.


17

Um Ihre Bearbeitung zu beantworten und zu erklären, warum Sie gezwungen sind, beide zu überschreiben, wenn Sie eine überschreiben, liegt alles in der Vererbung.

Wenn Sie == überschreiben und höchstwahrscheinlich eine Art semantische oder strukturelle Gleichheit bereitstellen (z. B. sind DateTimes gleich, wenn ihre InternalTicks-Eigenschaften gleich sind, auch wenn sie unterschiedliche Instanzen sein können), ändern Sie das Standardverhalten des Operators von Objekt, das das übergeordnete Objekt aller .NET-Objekte ist. Der Operator == ist in C # eine Methode, deren Basisimplementierung Object.operator (==) einen referenziellen Vergleich durchführt. Object.operator (! =) Ist eine andere, andere Methode, die auch einen referenziellen Vergleich durchführt.

In fast jedem anderen Fall des Überschreibens von Methoden wäre es unlogisch anzunehmen, dass das Überschreiben einer Methode auch zu einer Verhaltensänderung einer antonymischen Methode führen würde. Wenn Sie eine Klasse mit den Methoden Increment () und Decrement () erstellen und Increment () in einer untergeordneten Klasse überschreiben würden, würden Sie erwarten, dass Decrement () auch mit dem Gegenteil Ihres überschriebenen Verhaltens überschrieben wird? Der Compiler kann nicht intelligent genug gemacht werden, um in allen möglichen Fällen eine Umkehrfunktion für eine Implementierung eines Operators zu generieren.

Obwohl Operatoren den Methoden sehr ähnlich implementiert sind, arbeiten sie konzeptionell paarweise. == und! =, <und> und <= und> =. In diesem Fall wäre es aus Sicht eines Verbrauchers unlogisch zu glauben, dass! = Anders funktioniert als ==. Der Compiler kann also nicht in allen Fällen davon ausgehen, dass a! = B ==! (A == b), aber es wird allgemein erwartet, dass == und! = Auf ähnliche Weise funktionieren, sodass der Compiler dies erzwingt Sie müssen paarweise implementieren, aber am Ende tun Sie das tatsächlich. Wenn für Ihre Klasse a! = B ==! (A == b), implementieren Sie einfach den Operator! = Mit! (==), aber wenn diese Regel nicht in allen Fällen für Ihr Objekt gilt (zum Beispiel Wenn der Vergleich mit einem bestimmten Wert (gleich oder ungleich) nicht gültig ist, müssen Sie schlauer als die IDE sein.

Die WIRKLICHE Frage, die gestellt werden sollte, ist, warum <und> und <= und> = Paare für Vergleichsoperatoren sind, die gleichzeitig implementiert werden müssen, wenn sie numerisch ausgedrückt werden! (A <b) == a> = b und! (A>) b) == a <= b. Sie sollten alle vier implementieren müssen, wenn Sie eine überschreiben, und Sie sollten wahrscheinlich auch == (und! =) Überschreiben müssen, weil (a <= b) == (a == b), wenn a semantisch ist gleich b.


14

Wenn Sie == für Ihren benutzerdefinierten Typ überladen und nicht! =, Wird dies vom Operator! = Für Objekt! = Objekt behandelt, da alles vom Objekt abgeleitet ist, und dies würde sich stark von CustomType! = CustomType unterscheiden.

Auch die Sprachentwickler wollten es wahrscheinlich auf diese Weise, um den Programmierern die größtmögliche Flexibilität zu ermöglichen, und auch, damit sie keine Annahmen darüber treffen, was Sie vorhaben.


Sie haben diese Frage inspiriert: stackoverflow.com/questions/6919259/…
Brian Gordon

2
Der Grund für die Flexibilität kann auch auf eine Vielzahl von Funktionen angewendet werden, die sie weggelassen haben , z. B. blogs.msdn.com/b/ericlippert/archive/2011/06/23/…. Daher erklärt dies nicht ihre Wahl für die Implementierung von Gleichstellungsoperatoren.
Stefan Dragnev

Das ist sehr interessant. Scheint, als wäre es eine nützliche Funktion. Ich habe keine Ahnung, warum sie das ausgelassen hätten.
Chris Mullins

11

Das fällt mir zuerst ein:

  • Was ist, wenn das Testen der Ungleichheit viel schneller ist als das Testen der Gleichheit?
  • Was ist, wenn Sie in einigen Fällen beide für und zurückgeben möchtenfalse==!= (dh wenn sie aus irgendeinem Grund nicht verglichen werden können) ?

5
Im ersten Fall könnten Sie dann Gleichheitstests in Bezug auf Ungleichheitstests implementieren.
Stefan Dragnev

Der zweite Fall bricht Strukturen wie Dictionaryund Listwenn Sie Ihren benutzerdefinierten Typ mit ihnen verwenden.
Stefan Dragnev

2
@Stefan: Dictionaryverwendet GetHashCodeund Equalsnicht die Operatoren. Listverwendet Equals.
Dan Abramov

6

Nun, es ist wahrscheinlich nur eine Design-Wahl, aber wie Sie sagen, x!= ymuss es nicht dasselbe sein wie!(x == y) . Wenn Sie keine Standardimplementierung hinzufügen, können Sie sicher sein, dass Sie nicht vergessen, eine bestimmte Implementierung zu implementieren. Und wenn es tatsächlich so trivial ist, wie Sie sagen, können Sie einfach eins mit dem anderen implementieren. Ich verstehe nicht, wie schlecht das ist.

Es kann auch einige andere Unterschiede zwischen C # und Lua geben ...


1
Es ist eine ausgezeichnete Praxis, eine in Bezug auf die andere zu implementieren. Ich habe gerade gesehen, dass es anders gemacht wurde - was fehleranfällig ist.
Stefan Dragnev

3
Das ist das Problem des Programmierers, nicht das Problem von C #. Programmierer, die dieses Recht nicht umsetzen, werden auch in anderen Bereichen scheitern.
GolezTrol

@GolezTrol: Könnten Sie diese Lua-Referenz erklären? Ich kenne Lua überhaupt nicht, aber Ihre Referenz scheint auf etwas hinzudeuten.
zw324

@Ziyao Wei: OP weist darauf hin, dass Lua dies anders als C # macht (Lua fügt anscheinend Standardgegenstücke für die genannten Operatoren hinzu). Ich versuche nur, einen solchen Vergleich zu trivialisieren. Wenn es überhaupt keine Unterschiede gibt, hat mindestens einer von ihnen kein Existenzrecht. Ich muss sagen, ich kenne Lua auch nicht.
GolezTrol

6

Die Schlüsselwörter in Ihrer Frage sind " Warum " und " Muss ".

Als Ergebnis:

Es ist wahr, es so zu beantworten, weil sie es so entworfen haben ... aber nicht den "Warum" -Teil Ihrer Frage zu beantworten.

Die Antwort, dass es manchmal hilfreich sein kann, beide unabhängig voneinander zu überschreiben, ist wahr ... aber nicht die Beantwortung des "Muss" -Teils Ihrer Frage.

Ich denke , die einfache Antwort ist , dass es nicht ist kein überzeugender Grund , warum C # erfordert Sie beide außer Kraft zu setzen.

Die Sprache sollte es Ihnen ermöglicht , nur außer Kraft zu setzen ==, und bieten Ihnen eine Standardimplementierung !=das ist !das. Wenn Sie zufällig auch überschreiben möchten !=, haben Sie es.

Es war keine gute Entscheidung. Menschen entwerfen Sprachen, Menschen sind nicht perfekt, C # ist nicht perfekt. Achselzucken und QED


5

Nur um die hervorragenden Antworten hier zu ergänzen:

Überlegen Sie, was im Debugger passieren würde , wenn Sie versuchen, in einen !=Operator einzusteigen und in einem zu landen== stattdessen ! Sprechen Sie über Verwirrung!

Es ist sinnvoll, dass CLR Ihnen die Freiheit gibt, den einen oder anderen Operator wegzulassen, da es mit vielen Sprachen funktionieren muss. Aber es gibt viele Beispiele für C # nicht CLR Funktionen aussetzt ( refkehrt und Einheimischer, zum Beispiel), und viele Beispiele von Funktionen , die nicht in dem CLR Implementierung selbst (zB: using, lock, foreach, etc.).


3

Programmiersprachen sind syntaktische Neuanordnungen außergewöhnlich komplexer logischer Anweisungen. Können Sie in diesem Sinne einen Fall der Gleichheit definieren, ohne einen Fall der Ungleichheit zu definieren? Die Antwort ist nein. Damit ein Objekt a gleich Objekt b ist, muss auch die Umkehrung von Objekt a ungleich b wahr sein. Eine andere Möglichkeit, dies zu zeigen, ist

if a == b then !(a != b)

Dies bietet der Sprache die eindeutige Fähigkeit, die Gleichheit von Objekten zu bestimmen. Zum Beispiel kann der Vergleich NULL! = NULL einen Schraubenschlüssel in die Definition eines Gleichheitssystems werfen, das keine Nichtgleichheitsanweisung implementiert.

Nun, in Bezug auf die Idee von! = Einfach austauschbare Definition wie in

if !(a==b) then a!=b

Damit kann ich nicht streiten. Es war jedoch höchstwahrscheinlich eine Entscheidung der C # -Sprachenspezifikationsgruppe, dass der Programmierer gezwungen sein sollte, die Gleichheit und Nichtgleichheit eines Objekts explizit zusammen zu definieren


Nullwäre nur ein Problem, weil nulles sich um eine gültige Eingabe handelt ==, die es nicht sein sollte, obwohl es offensichtlich sehr praktisch ist. Persönlich denke ich nur, dass es Unmengen von vagen, schlampigen Programmen erlaubt.
nicodemus13

2

Kurz gesagt, erzwungene Konsistenz.

'==' und '! =' sind immer wahre Gegensätze, egal wie Sie sie definieren, definiert als solche durch ihre verbale Definition von "gleich" und "nicht gleich". Indem Sie nur einen von ihnen definieren, öffnen Sie sich einer Inkonsistenz des Gleichheitsoperators, bei der sowohl '==' als auch '! =' Für zwei gegebene Werte beide wahr oder beide falsch sein können. Sie müssen beide definieren, da Sie bei der Definition des einen auch den anderen entsprechend definieren müssen, damit klar ist, wie Sie "Gleichheit" definieren. Die andere Lösung für den Compiler besteht darin, nur zuzulassen, dass Sie '==' OR '! =' Überschreiben und den anderen als inhärent negierend für den anderen belassen. Offensichtlich ist das beim C # -Compiler nicht der Fall, und da bin ich mir sicher. '

Die Frage, die Sie stellen sollten, lautet: "Warum muss ich die Operatoren überschreiben?" Das ist eine starke Entscheidung, die eine starke Argumentation erfordert. Für Objekte vergleichen '==' und '! =' Als Referenz. Wenn Sie sie überschreiben möchten, um sie NICHT anhand einer Referenz zu vergleichen, erstellen Sie eine allgemeine Operatorinkonsistenz, die für keinen anderen Entwickler erkennbar ist, der diesen Code lesen würde. Wenn Sie versuchen, die Frage zu stellen, ob der Status dieser beiden Instanzen äquivalent ist, sollten Sie IEquatible implementieren, Equals () definieren und diesen Methodenaufruf verwenden.

Schließlich definiert IEquatable () NotEquals () nicht aus demselben Grund: Es kann zu Inkonsistenzen bei Gleichheitsoperatoren kommen. NotEquals () sollte IMMER zurückkehren! Equals (). Indem Sie die Definition von NotEquals () für die Klasse öffnen, die Equals () implementiert, erzwingen Sie erneut das Problem der Konsistenz bei der Bestimmung der Gleichheit.

Edit: Dies ist einfach meine Argumentation.


Das ist unlogisch. Wenn der Grund Konsistenz wäre, würde das Gegenteil zutreffen: Sie könnten nur implementieren operator ==und der Compiler würde daraus schließen operator !=. Immerhin ist es das, was es für operator +und tut operator +=.
Konrad Rudolph

1
foo + = bar; ist eine zusammengesetzte Zuordnung, Abkürzung für foo = foo + bar;. Es ist kein Operator.
Blenderfreaky

Wenn in der Tat sie sind ‚immer wahr Gegensätze‘, dann wäre das ein Grund sein , um automatisch zu definieren , a != bwie !(a == b)... (das ist nicht das, was C # tut).
Andrewf

-3

Wahrscheinlich hatte nur etwas, an das sie nicht gedacht hatten, keine Zeit zu tun.

Ich benutze immer deine Methode, wenn ich == überlade. Dann benutze ich es einfach in der anderen.

Sie haben Recht, mit ein wenig Arbeit könnte der Compiler uns dies kostenlos zur Verfügung stellen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.