Warum unterscheiden sich die folgenden Ausdrücke?
[1] (object)0 == (object)0 //false
[2] ((object)0).Equals((object)0) // true
Eigentlich kann ich [1] völlig verstehen, da wahrscheinlich die .NET-Laufzeit box
die Ganzzahl ist und stattdessen die Referenzen vergleicht. Aber warum ist [2] anders?
short myShort = 0; int myInt = 0; Console.WriteLine("{0}{1}{2}", myShort.Equals(myInt), myInt.Equals(myShort), myInt == myShort);
Überprüfen Sie es jetzt anhand der Realität. War Ihre Vorhersage richtig? Wenn nicht, können Sie die Diskrepanz erklären?int16
akashort
Equals-Methode, dann siehe msdn.microsoft.com/en-us/library/ms173105.aspx . Ich möchte Eric Lipperts Rätsel nicht verderben, aber es sollte ziemlich einfach sein, es herauszufinden, wenn Sie diese Seiten gelesen haben.((Integer)0)==((Integer)0)
es als wahr ausgewertet wird.IFormattable x = 0; bool test = (object)x == (object)x;
. Es wird kein neues Boxen durchgeführt, wenn sich die Struktur bereits in einer Box befindet.Antworten:
Der Grund, warum sich die Aufrufe unterschiedlich verhalten, ist, dass sie an sehr unterschiedliche Methoden gebunden sind.
Der
==
Fall wird an den Gleichheitsoperator für statische Referenzen gebunden. Es werden 2 unabhängige Boxwerteint
erstellt, daher handelt es sich nicht um dieselbe Referenz.Im zweiten Fall binden Sie an die Instanzmethode
Object.Equals
. Dies ist eine virtuelle Methode, die nach unten filtertInt32.Equals
und nach einer Ganzzahl mit Kästchen sucht. Beide ganzzahligen Werte sind 0, daher sind sie gleichquelle
==
Fall ruft nicht anObject.ReferenceEquals
. Es wird einfach derceq
IL-Befehl erzeugt, um einen Referenzvergleich durchzuführen.==
macht das gleiche wieReferenceEquals
(auf Objekt jedenfalls). Es ist alles nur eine interne Optimierung auf MS-Seite, um unnötige interne Funktionsaufrufe für häufig verwendete Funktionen zu vermeiden.bool operator ==(object x, object y);
bool operator !=(object x, object y);
Die Operatoren geben das Ergebnis des Vergleichs der beiden Referenzen auf Gleichheit oder Nichtgleichheit zurück. Es ist nicht erforderlich, dass die MethodeSystem.Object.ReferenceEquals
zur Bestimmung des Ergebnisses verwendet wird. Zu @markmnl: Nein, der C # -Compiler inline nicht, das ist etwas, was der Jitter manchmal tut (aber in diesem Fall nicht). 280Z28 ist also richtig, dieReferenceEquals
Methode wird eigentlich nicht verwendet.Cat Whiskers; Dog Fido; IDog Fred;
(für nicht verwandte SchnittstellenICat
undIDog
und nicht verwandte KlassenCat:ICat
undDog:IDog
) wären die Vergleiche legalWhiskers==Fido
undWhiskers==34
legal (der erste könnte nur wahr sein, wenn Whiskers und Fido beide null wären, der zweite könnte niemals wahr sein ). Tatsächlich lehnt ein C # -Compiler beide ab.Whiskers==Fred;
wird verboten, wennCat
es versiegelt ist, aber erlaubt, wenn es nicht ist.Wenn Sie den int-Wert
0
(oder einen anderen Werttyp) in umwandelnobject
, wird der Wert eingerahmt . Jede Umwandlungobject
erzeugt eine andere Box (dh eine andere Objektinstanz). Der==
Operator für denobject
Typ führt einen Referenzvergleich durch, gibt also false zurück, da die linke und die rechte Seite nicht dieselbe Instanz sind.Wenn Sie dagegen
Equals
eine virtuelle Methode verwenden, wird die Implementierung des tatsächlichen Box-Typs verwendet, dhInt32.Equals
, der true zurückgibt, da beide Objekte denselben Wert haben.quelle
Der
==
Operator ist statisch und nicht virtuell. Es wird der genaue Code ausgeführt, den dieobject
Klasse definiert (`Objekt ist der Kompilierungszeittyp der Operanden), der unabhängig vom Laufzeittyp eines der beiden Objekte einen Referenzvergleich durchführt.Die
Equals
Methode ist eine virtuelle Instanzmethode. Es wird der Code ausgeführt, der im tatsächlichen Laufzeittyp des (ersten) Objekts definiert ist, nicht der Code in derobject
Klasse. In diesem Fall ist das Objekt ein Objektint
, daher wird ein Wertevergleich durchgeführt, da dies vomint
Typ für seineEquals
Methode definiert wird.quelle
==
Token repräsentiert tatsächlich zwei Operatoren, von denen einer überladbar ist und einer nicht. Das Verhalten des zweiten Operators unterscheidet sich stark von dem einer Überladung von (Objekt, Objekt).Die
Equals()
Methode ist virtuell.Daher wird die konkrete Implementierung immer aufgerufen, auch wenn die Call-Site gegossen wird
object
.int
ÜberschreibtEquals()
den Vergleich nach Wert, sodass Sie einen Wertvergleich erhalten.quelle
==
Verwenden:Object.ReferenceEquals
Object.Equals
vergleicht den Wert.Die
object.ReferenceEquals
Methode vergleicht Referenzen. Wenn Sie ein Objekt zuweisen, erhalten Sie zusätzlich zu den Daten des Objekts auf dem Speicherheap eine Referenz, die einen Wert enthält, der seinen Speicherort angibt.Die
object.Equals
Methode vergleicht den Inhalt von Objekten. Zunächst wird geprüft, ob die Referenzen gleich sind, ebenso wie object.ReferenceEquals. Dann werden jedoch abgeleitete Equals-Methoden aufgerufen, um die Gleichheit weiter zu testen. Sieh dir das an:quelle
Object.ReferenceEquals
sich wie eine Methode verhält, die den C #==
-Operator für ihre Operanden verwendet, verwendet der C # -Referenzgleichheitsoperatoroperator (der durch die Verwendung==
von Operandentypen dargestellt wird, für die keine Überladung definiert ist) eine spezielle Anweisung, anstatt aufzurufenReferenceEquals
. Des Weiteren ,Object.ReferenceEquals
wenn beide werden Operanden akzeptieren , die nur passieren passen könnte null sein und Operanden akzeptieren , die sein müssen typ gezwungen zuObject
und konnte somit nicht möglicherweise nichts gefunden, während die Referenzstellungs Version==
würde sich weigern , eine solche Nutzung zu kompilieren .Der C # -Operator verwendet das Token
==
, um zwei verschiedene Operatoren darzustellen: einen statisch überladbaren Vergleichsoperator und einen nicht überladbaren Referenzvergleichsoperator. Wenn es auf das==
Token stößt , prüft es zunächst, ob eine Überlastung des Gleichheitstests vorliegt, die für die Operandentypen gilt. In diesem Fall wird diese Überlastung ausgelöst. Andernfalls wird geprüft, ob die Typen für den Referenzvergleichsoperator gelten. In diesem Fall wird dieser Operator verwendet. Wenn keiner der Operatoren auf die Operandentypen anwendbar ist, schlägt die Kompilierung fehl.Der Code
(Object)0
sendet nicht nur einInt32
toObject
:Int32
wie alle Werttypen tatsächlich zwei Typen, von denen einer Werte und Speicherorte beschreibt (wie die wörtliche Null), sondern von nichts abgeleitet ist und einer beschreibt Haufen Objekte und stammt vonObject
; Da nur der letztere Typ gesendet werden kann,Object
muss der Compiler ein neues Heap-Objekt dieses letzteren Typs erstellen. Jeder Aufruf von(Object)0
erstellt ein neues Heap-Objekt, sodass die beiden Operanden==
unterschiedliche Objekte sind, von denen jedes unabhängig denInt32
Wert 0 kapselt .Für die Klasse
Object
sind keine verwendbaren Überladungen für den Operator equals definiert. Folglich kann der Compiler den überladenen Gleichheitstestoperator nicht verwenden und greift auf die Verwendung des Referenzgleichheitstests zurück. Da sich die beiden Operanden==
auf unterschiedliche Objekte beziehen, wird ein Bericht erstelltfalse
. Der zweite Vergleich ist erfolgreich, da eine Heap-Objekt-InstanzInt32
gefragt wird, ob sie der anderen entspricht. Da diese Instanz weiß, was es bedeutet, einer anderen bestimmten Instanz gleich zu sein, kann sie antwortentrue
.quelle
0
gehe ich davon aus , dass jedes Mal, wenn Sie ein Literal in Ihren Code schreiben, ein int-Objekt im Heap dafür erstellt wird. Es ist keine eindeutige Referenz auf einen globalen statischen Nullwert (wie sie String.Empty gemacht haben, um zu vermeiden, dass neue leere String-Objekte nur zum Initialisieren neuer Strings erstellt werden). Ich bin mir also ziemlich sicher, dass selbst das Ausführen von a0.ReferenceEquals(0)
false zurückgibt, da beide Nullen sind neu erstellteInt32
Objekte.0.ReferenceEquals(0)
weil Sie versuchen, eine Methode für eine Kompilierungszeitkonstante aufzurufen. Es gibt kein Objekt zum Aufhängen. Ein int ohne Box ist eine Struktur, die auf dem Stapel gespeichert ist. Auchint i = 0; i.ReferenceEquals(...)
wird nicht funktionieren. WeilSystem.Int32
erbt NICHT vonObject
.System.Int32
ist einstruct
, einstruct
istSystem.ValueType
, das selbst erbtSystem.Object
. Deshalb gibt es eineToString()
Methode und eineEquals
Methode fürSystem.Int32
Object
aktiviert, und hier habe ich vor langer Zeit aufgehört, mir darüber Sorgen zu machen. Es war nie notwendig, weiter zu gehen. AFAIK die CLR behandelt die beiden Typen unterschiedlich und speziell. Es ist nicht nur Vererbung. Es ist jedochis
eine von zwei Arten von Daten. Ich wollte nur nicht, dass jemand diesen Kommentar liest und so weit vom Kurs abweicht, einschließlich der Seltsamkeit gegenüber leeren Strings, die das Internieren von Strings ignoriert.Beide Prüfungen sind unterschiedlich. Der erste prüft auf Identität , der zweite auf Gleichheit . Im Allgemeinen sind zwei Begriffe identisch, wenn sie sich auf dasselbe Objekt beziehen. Dies impliziert, dass sie gleich sind. Zwei Terme sind gleich, wenn ihre Werte gleich sind.
In Bezug auf die Programmierung wird Identität normalerweise durch Referenzgleichheit beeinträchtigt. Wenn der Zeiger auf beide Begriffe gleich (!) Ist, ist das Objekt, auf das sie zeigen, genau das gleiche. Wenn sich die Zeiger jedoch unterscheiden, kann der Wert der Objekte, auf die sie zeigen, immer noch gleich sein. In C # kann die Identität mit dem statischen
Object.ReferenceEquals
Element überprüft werden, während die Gleichheit mit dem nicht statischen Element überprüft wirdObject.Equals
. Da Sie zwei Ganzzahlen in Objekte umwandeln (was übrigens als "Boxen" bezeichnet wird), führt der Operator==
vonobject
die erste Prüfung durch, die standardmäßig zugeordnet ist,Object.ReferenceEquals
und prüft die Identität. Wenn Sie das nicht statischeEquals
Mitglied explizit aufrufen , führt der dynamische Versand zu einem Aufruf vonInt32.Equals
, der die Gleichheit überprüft.Beide Konzepte sind ähnlich, aber nicht gleich. Sie mögen zunächst verwirrend erscheinen, aber der kleine Unterschied ist sehr wichtig! Stellen Sie sich zwei Personen vor, nämlich "Alice" und "Bob". Sie leben beide in einem gelben Haus. Unter der Annahme, dass Alice und Bob in einem Viertel leben, in dem Häuser nur in ihrer Farbe unterschiedlich sind, könnten sie beide in verschiedenen gelben Häusern leben. Wenn Sie beide Häuser vergleichen, werden Sie feststellen, dass sie absolut gleich sind, weil sie beide gelb sind! Sie teilen sich jedoch nicht dasselbe Haus und daher sind ihre Häuser gleich , aber nicht identisch . Identität würde bedeuten, dass sie im selben Haus leben.
Hinweis : Einige Sprachen definieren den
===
Operator, der auf Identität überprüft werden soll.quelle