358 Stimmen

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

Der C#-Compiler verlangt, dass immer dann, wenn ein benutzerdefinierter Typ den Operator == muss sie auch definieren != (siehe hier ).

Warum?

Ich bin neugierig, warum die Entwickler dies für notwendig hielten und warum der Compiler nicht auf eine vernünftige Implementierung für einen der beiden Operatoren zurückgreifen kann, wenn nur der andere vorhanden ist. In Lua kann man zum Beispiel nur den Gleichheitsoperator definieren und bekommt den anderen umsonst. C# könnte dasselbe tun, indem es Sie auffordert, entweder == oder sowohl == als auch != zu definieren, und dann den fehlenden !=-Operator automatisch kompiliert als !(left == right) .

Ich verstehe, dass es seltsame Eckfälle gibt, in denen einige Einheiten weder gleich noch ungleich sein können (wie IEEE-754 NaN's), aber diese scheinen die Ausnahme zu sein, nicht die Regel. Das erklärt also nicht, warum die Entwickler des C#-Compilers die Ausnahme zur Regel gemacht haben.

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

Anmerkung: Das Gleiche gilt für die Operatoren < > <= >=. Ich kann mir keine Fälle vorstellen, in denen man diese auf unnatürliche Weise definieren muss. Lua lässt Sie nur < und <= definieren und definiert >= und > auf natürliche Weise durch die Negation der Formers. Warum tut C# nicht dasselbe (zumindest "standardmäßig")?

EDIT

Offensichtlich gibt es triftige Gründe dafür, dem Programmierer zu erlauben, Prüfungen auf Gleichheit und Ungleichheit nach Belieben zu implementieren. Einige der Antworten weisen auf Fälle hin, in denen das gut sein könnte.

Der Kern meiner Frage ist jedoch, warum dies zwangsweise in C# erforderlich ist, wenn in der Regel es ist nicht logischerweise notwendig?

Es steht auch in auffälligem Kontrast zu den Designentscheidungen für .NET-Schnittstellen wie Object.Equals , IEquatable.Equals IEqualityComparer.Equals wo das Fehlen eines NotEquals Gegenstück zeigt, dass der Rahmen Folgendes berücksichtigt !Equals() Objekte als ungleich und das war's. Außerdem werden Klassen wie Dictionary und Methoden wie .Contains() hängen ausschließlich von den vorgenannten Schnittstellen ab und verwenden die Operatoren nicht direkt, auch wenn sie definiert sind. In der Tat, wenn ReSharper Gleichheitsmitglieder erzeugt, definiert es sowohl == y != in Bezug auf Equals() und selbst dann nur, wenn der Benutzer beschließt, überhaupt Operatoren zu erzeugen. Die Gleichheitsoperatoren werden vom Framework nicht benötigt, um Objektgleichheit zu verstehen.

Im Grunde genommen kümmert sich das .NET-Framework nicht um diese Operatoren, es kümmert sich nur um ein paar Equals Methoden. Die Entscheidung, dass die Operatoren == und != vom Benutzer gemeinsam definiert werden müssen, hat rein mit dem Sprachdesign zu tun und nicht mit der Objektsemantik, soweit es .NET betrifft.

28 Stimmen

+1 für eine ausgezeichnete Frage. Es scheint überhaupt keinen Grund zu geben... natürlich könnte der Compiler davon ausgehen, dass a != b in !(a == b) übersetzt werden kann, solange ihm nichts anderes gesagt wird. Ich bin auch neugierig, warum man sich dazu entschlossen hat, dies zu tun.

0 Stimmen

Wenn es so einfach ist wie !(left == right) dann ist das kein Problem für Sie. Wenn die Negation viel komplizierter ist, hat der Compiler keine Chance zu optimieren - aber Sie. Daher ist es nur konsequent, Sie zu zwingen, beide Fälle selbst zu behandeln.

3 Stimmen

Die goldene Regel Nr. 1 bei der Entwicklung von Software, APIs und GUIs lautet: Machen Sie immer den Normalfall zum Standard! Es sieht so aus, als hätte das jemand versehentlich nicht getan.

14voto

Chris Mullins Punkte 6427

Wenn Sie == für Ihren benutzerdefinierten Typ überladen und nicht !=, dann wird es vom !=-Operator für object != object behandelt, da alles von object abgeleitet ist, und das wäre etwas ganz anderes als CustomType != CustomType.

Auch die Entwickler der Sprache haben es wahrscheinlich so gewollt, um den Programmierern die größtmögliche Flexibilität zu geben, und auch, damit sie keine Vermutungen darüber anstellen, was Sie zu tun beabsichtigen.

3 Stimmen

Der Grund für die Flexibilität lässt sich auch auf eine Vielzahl von Funktionen anwenden, die nicht berücksichtigt wurden, z. B. blogs.msdn.com/b/ericlippert/archive/2011/06/23/ Es erklärt also nicht, warum sie sich für die Implementierung von Gleichheitsoperatoren entschieden haben.

11voto

Dan Abramov Punkte 252334

Das ist das, was mir als erstes in den Sinn kommt:

  • Was wäre, wenn die Prüfung der Ungleichheit viel schneller geht als die Prüfung der Gleichheit?
  • Was wäre, wenn in einigen Fällen Sie möchten zurückkehren false sowohl für == y != (d.h. wenn sie aus irgendeinem Grund nicht verglichen werden können)

7 Stimmen

Im ersten Fall könnten Sie die Gleichheitsprüfung in Form einer Ungleichheitsprüfung durchführen.

0 Stimmen

Im zweiten Fall werden Strukturen wie Dictionary y List wenn Sie Ihren benutzerdefinierten Typ mit ihnen verwenden.

2 Stimmen

@Stefan: Dictionary verwendet GetHashCode y Equals und nicht die Betreiber. List verwendet Equals .

7voto

Greg Hendershott Punkte 15672

Die Schlüsselwörter in Ihrer Frage sind " warum " und " ねばならない ".

Das Ergebnis ist:

Die Antwort, dass es so ist, weil sie es so geplant haben, ist wahr ... aber sie beantwortet nicht den "Warum"-Teil Ihrer Frage.

Die Antwort, dass es manchmal hilfreich sein kann, beide unabhängig voneinander außer Kraft zu setzen, ist wahr ... aber keine Antwort auf den "muss"-Teil Ihrer Frage.

Ich denke, die einfache Antwort ist, dass es ist nicht einen überzeugenden Grund, warum C# erfordert Sie können beides außer Kraft setzen.

Die Sprache sollte es Ihnen ermöglichen, nur folgende Punkte zu überschreiben == und bieten Ihnen eine Standardimplementierung von != das ist ! das. Wenn Sie das überschreiben möchten != auch, nur zu.

Das war keine gute Entscheidung. Menschen entwerfen Sprachen, Menschen sind nicht perfekt, C# ist nicht perfekt. Shrug und Q.E.D.

6voto

GolezTrol Punkte 111587

Nun, das ist wahrscheinlich nur eine Designentscheidung, aber wie Sie sagen, x!= y muss nicht dasselbe sein wie !(x == y) . Indem Sie keine Standardimplementierung hinzufügen, sind Sie sicher, dass Sie nicht vergessen können, eine bestimmte Implementierung zu implementieren. Und wenn es tatsächlich so trivial ist, wie Sie sagen, können Sie die eine Implementierung einfach durch die andere ersetzen. Ich verstehe nicht, was daran "schlechte Praxis" sein soll.

Es kann einige andere Unterschiede zwischen C# und Lua zu sein...

1 Stimmen

Es ist eine hervorragende Übung, das eine in Bezug auf das andere umzusetzen. Ich habe nur gesehen, dass es anders gemacht wurde - was fehleranfällig ist.

3 Stimmen

Das ist das Problem des Programmierers, nicht das Problem von C#. Programmierer, die es nicht schaffen, dies richtig zu implementieren, werden auch in anderen Bereichen versagen.

0 Stimmen

@GolezTrol: Könnten Sie diese Lua-Referenz erläutern? Ich bin mit Lua überhaupt nicht vertraut, aber Ihr Verweis scheint auf etwas hinzudeuten.

5voto

Andrew Russell Punkte 26537

Ich möchte mich den hervorragenden Antworten hier anschließen:

Überlegen Sie, was passieren würde, wenn Debugger wenn Sie versuchen, in eine != Betreiber und landen in einer == Operator stattdessen! Das ist verwirrend!

Es ist sinnvoll, dass die CLR Ihnen die Freiheit gibt, den einen oder anderen Operator wegzulassen, da sie mit vielen Sprachen arbeiten muss. Aber es gibt viele Beispiele dafür, dass C# CLR-Funktionen nicht offenlegt ( ref returns und locals, zum Beispiel) und viele Beispiele für die Implementierung von Funktionen, die nicht in der CLR selbst enthalten sind (z. B: using , lock , foreach etc.).

1 Stimmen

Das scheint mir tatsächlich das Schlimmste zu sein. . Auswirkung von automatisch generierten Operatoren haben würde. Ich glaube aber nicht, dass dies ihre Argumentation war. Es gibt noch andere Beispiele, bei denen Code ausgeführt wird, den man nicht explizit aufgerufen hat, wie z. B. die Initialisierung eines statischen Mitglieds einer Klasse, mit der man nicht lokal arbeitet, sondern die man lädt, ohne darüber nachzudenken. Daran gewöhnt man sich leicht, nachdem man die Gründe dafür kennengelernt hat. Ich glaube nicht, dass die Leute zu sehr verwirrt wären, warum == wird aufgerufen, wenn sie prüfen, ob != ohne sie zu definieren. Ich nehme an, sie werden es verstehen, spätestens nachdem sie die Unterlagen dazu gelesen haben.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X