Warum haben diese beiden Vergleiche unterschiedliche Ergebnisse?

68

Warum gibt dieser Code true zurück:

new Byte() == new Byte()   // returns true

Dieser Code gibt jedoch false zurück:

new Byte[0] == new Byte[0] // returns false
Maxim Zhukov
quelle
4
Ich bin niemand überrascht Duplikate für diese Frage gefunden, da es sehr einfach ist Wert Typ Vers Referenztyp Frage.
Ganesh Jadhav
2
Ich bin noch mehr überrascht über die Anzahl der Up-Votes - 51 ab sofort.
Ganesh Jadhav

Antworten:

143

Da new Byte()erstellt Werttyp, die nach Wert verglichen werden (standardmäßig wird bytemit Wert zurückgegeben 0). Und new Byte[0]erstellt ein Array, das ein Referenztyp ist und mit einer Referenz verglichen wird (und diese beiden Instanzen des Arrays haben unterschiedliche Referenzen).

Weitere Informationen finden Sie im Artikel Werttypen und Referenztypen .

Sergey Berezovskiy
quelle
43

Bytes sind Werttypen in .NET. Dies bedeutet, dass der ==Operator genau dann true zurückgibt, wenn die beiden Bytes denselben Wert haben. Dies wird auch als Wertgleichheit bezeichnet .

Arrays sind jedoch Referenztypen in .NET. Dies bedeutet, dass der ==Operator genau dann true zurückgibt, wenn sie auf dieselbe Array-Instanz im Speicher verweisen. Dies wird auch als Referenzgleichheit oder Identität bezeichnet .

Beachten Sie, dass der ==Operator sowohl für Referenz- als auch für Werttypen überladen werden kann. System.StringDies ist beispielsweise ein Referenztyp, aber der ==Operator für Zeichenfolgen vergleicht jedes Zeichen im Array nacheinander. Siehe Richtlinien zum Überladen von Equals () und Operator == (C # -Programmierhandbuch) .

Wenn Sie testen möchten, ob die Arrays (in der richtigen Reihenfolge) genau dieselben Werte enthalten , sollten Sie die Verwendung von Enumerable.SequenceEqualanstelle von in Betracht ziehen ==.

pswg
quelle
1
Ich glaube, der Kern der Frage ist der ==Betreiber und seine duale Natur. Diese Antwort deckt das klar ab.
rory.ap
Ich mag die Verwendung von "standardmäßig" für andere Referenztypen, aber ist es tatsächlich möglich, dieses Verhalten für Array-Typen zu ändern?
Chris Hayes
@ChrisHayes Nein. Operatoren dürfen nur innerhalb der Klasse (n) überladen werden, für die sie definiert sind. Da die System.ArrayKlasse keine Überladung bereitstellt, wird die Standardreferenzgleichheit verwendet. Sie könnten denken, Sie könnten Ihren eigenen Array-Typ erstellen ( System.Arrayist schließlich abstrakt), aber der Compiler erlaubt Ihnen nicht, davon zu erben. Sie könnten wahrscheinlich mit einer subtilen Verwendung impliziter Operatoren ziemlich nahe kommen, um das Array in einen anderen Typ umzuwandeln, aber genau die Idee lässt meine Haut kriechen.
Pswg
10

Beim Vergleichen der Referenz wird tatsächlich die Zeigeradresse verglichen, die unterschiedlich ist. Dies ist der Grund, warum false zurückgegeben wird, und bei der Wertadresse spielt es keine Rolle, ob der Wert verglichen wird.

Der Compiler versucht, den Werttyp in Registern zu speichern, aber aufgrund der begrenzten Registernummer erfolgt die weitere Speicherung im Stapel mit den Werten [Referenz], während sich der Referenztyp im Stapel befindet, der Wert jedoch eine Adresse der Speicheradresse im Heap enthält.

Vergleichen Sie hier den im Stapel vorhandenen Wert, der im ersten Fall für beide gleich ist, während im zweiten Fall die Adressen des Heaps unterschiedlich sind.

Referenz

Zaheer Ahmed
quelle
5
Dies ist eine ziemlich verwirrende Antwort. Im ersten Teil sieht es immer noch wie ein Referenzvergleich aus, da Sie immer noch das Wort "Zeiger" verwenden. Die Verwendung der Grafik im Vergleich zu nur Text ist ebenfalls ärgerlich, da es für mich sehr schwierig ist, sie zu bearbeiten, um die Antwort zu verbessern.
Chris Hayes
-1 zum Fortbestehen des Mythos "Werttypen werden im Stapel gespeichert". Ich hätte es für ziemlich wahrscheinlich gehalten, dass die Ergebnisse dieser beiden new Byte()Aufrufe wahrscheinlich in Registern gespeichert sind.
Damien_The_Unbeliever
@Damien_The_Unbeliever Der Registerspeicher hängt von der Verfügbarkeit des Registers ab, ansonsten wird er im Stapel gespeichert. In beiden Fällen ist der Wert gleich.
Zaheer Ahmed
Eine vollständige Erklärung finden Sie unter blogs.msdn.com/b/ericlippert/archive/2010/09/30/… .
Pswg
5
Ihre ganze Antwort ist immer noch ein Streifzug. Der Schlüsselaspekt von Werttypen ist, dass sie nach Wert verglichen werden. Es spielt keine Rolle, wo dieser Wert gespeichert ist. Sie können zwei Werttypen in Heap-zugewiesene Strukturen einfügen (entweder absichtlich oder aufgrund von Heben), und die Vergleiche basieren weiterhin auf ihrem Wert.
Damien_The_Unbeliever
1

Es gibt eine Überlastung des ==Operators, bei der beide Operanden vom Typ sind, byteund es wird implementiert, um den Wert jedes Bytes zu vergleichen. In diesem Fall haben Sie zwei Null-Bytes, die gleich sind.

Der ==Operator ist für Arrays nicht überladen, daher objectwird objectim zweiten Fall die Überladung mit zwei Operanden verwendet (da Arrays vom Typ sind ), und ihre Implementierung vergleicht die Verweise auf die beiden Objekte. Der Verweis auf die beiden Arrays ist unterschiedlich.

Es ist erwähnenswert, dass dies (direkt) nichts mit der Tatsache zu tun hat, dass bytees sich um einen Werttyp handelt und Arrays Referenztypen sind. Der ==Operator für bytehat nur dann eine Wertsemantik, weil bei dieser Implementierung eine bestimmte Überlastung des Operators vorliegt. Wenn diese Überladung nicht vorhanden wäre , gäbe es keine Überladung, für die zwei Bytes gültige Operanden wären, und als solche würde der Code überhaupt nicht kompiliert . Sie können dies leicht genug erkennen, indem Sie eine benutzerdefinierte structDatei erstellen und zwei Instanzen davon mit dem ==Operator vergleichen. Der Code wird nicht kompiliert, es sei denn, Sie stellen Ihre eigene Implementierung ==für diese Typen bereit .

Servieren
quelle