Warum kann C # zwei Objekttypen nicht miteinander vergleichen, VB jedoch nicht?

152

Ich habe zwei Objekte in C # und weiß nicht, ob es sich um einen Booleschen oder einen anderen Typ handelt. Wenn ich jedoch versuche, diese C # zu vergleichen, kann ich nicht die richtige Antwort geben. Ich habe den gleichen Code mit VB.NET ausprobiert und das hat es geschafft!

Kann mir jemand sagen, wie ich das beheben kann, wenn es eine Lösung gibt?

C #:

object a = true;
object b = true;
object c = false;
if (a == b) c = true;
MessageBox.Show(c.ToString()); //Outputs False !!

VB.NET:

Dim a As Object = True
Dim b As Object = True
Dim c As Object = False
If (a = b) Then c = True
MessageBox.Show(c.ToString()) '// Outputs True
Mohsen Sarkar
quelle
3
Was ist, wenn Sie den Gleichheitsvergleich ändern a.Equals(b)?
Jason Meckley
8
Dies ist eine gute Frage für pädagogische Zwecke.
Lobo
10
Weil Ihr VB.NET-Code nicht Ihrem C # -Code entspricht.
Sicherheitshund
9
Wenn Sie zuweisen, erhalten aSie Boxen und erstellen eine Box mit true. Wenn Sie zuweisen, erhalten bSie eine weitere Box, die ebenfalls enthält true. Wenn Sie vergleichen aund b, da beide vom Typ Kompilierungszeit sind object, rufen Sie die Überladung auf, operator ==(object, object)die in der C # -Sprachenspezifikation definiert ist. Diese Überladung prüft, ob die Referenzen auf dasselbe Objekt verweisen. Da Sie zwei Felder haben, ist das Ergebnis falseund die Anweisung "unter" ifwird nicht ausgeführt. Um dies besser zu verstehen, versuchen Sie, die Zuordnung folgendermaßen zu ändern b: object b = a;Jetzt haben Sie nur noch eine Box.
Jeppe Stig Nielsen
3
Ich hatte schon einmal Gelegenheit zu sagen: "Seien Sie vorsichtig, wenn Sie annehmen, dass VB.NET und C # dieselbe Sprache sind, die mit einem anderen Akzent gesprochen wird - sie sind es nicht"
AakashM

Antworten:

168

In C # führt der ==Operator (wenn er auf Referenztypausdrücke angewendet wird) eine Referenzgleichheitsprüfung durch, sofern diese nicht überladen ist . Sie vergleichen zwei Referenzen, die das Ergebnis von Box-Conversions sind. Dies sind also unterschiedliche Referenzen.

BEARBEITEN: Bei Typen, die das überladen ==, können Sie ein anderes Verhalten erzielen - dies basiert jedoch auf dem Typ der Ausdrücke zur Kompilierungszeit . Zum Beispiel stringbietet ==(string, string):

string x = new string("foo".ToCharArray());
string y = new string("foo".ToCharArray());
Console.WriteLine(x == y); // True
Console.WriteLine((object) x == (object) y); // False

Hier verwendet der erste Vergleich den überladenen Operator, der zweite den "Standard" -Referenzvergleich.

In VB =erledigt der Bediener viel mehr Arbeit - dies ist nicht einmal gleichbedeutend mit der Verwendung object.Equals(x, y), da Dinge wie der Option CompareVergleich von Text Einfluss haben können.

Im Grunde die Betreiber nicht funktionieren auf die gleiche Weise und sind nicht beabsichtigt , auf die gleiche Weise.

Jon Skeet
quelle
17
+1 Ich wusste, dass Sie da sein werden, Sie LIEBEN diese Art von mysteriösen Fragen :)
Abdusalam Ben Haj
3
@AbZy: Ich hatte gehofft, eine detailliertere Erklärung für =die Funktionsweise von VB liefern zu können , aber die Spezifikation ist nicht besonders klar.
Jon Skeet
Interessante Sache, aber das Ändern des Objekts in dynamisch verhält sich genauso wie VB
VladL
4
@VladL: Ja, denn dann wird es nach den Ausführungszeittypen gehen und den bool == boolVergleich durchführen.
Jon Skeet
1
@ Mahdi Lobo hat vielleicht Code bereitgestellt, aber seine Antwort ist im Gegensatz zu Jons auch falsch.
Servy
79

Zusätzlich zu Jons Antwort, die die C # -Seite der Dinge erklärt, macht VB Folgendes:

In VB mit prüft Option Strict Onein Vergleich über = immer auf Wertgleichheit und niemals auf Referenzgleichheit. Tatsächlich wird Ihr Code nach dem Wechsel nicht einmal kompiliert, Option Strict Onda er System.Objectkeinen definiert Operator=. Sie sollten diese Option immer aktiviert haben, da sie Fehler effektiver abfängt als eine Venusfliegenfalle (obwohl in Ihrem speziellen Fall dieses nachlässige Verhalten tatsächlich das Richtige tut). 1

Tatsächlich Option Strict Onverhält sich VB mit noch strenger als C #: In C # wird a == b entweder ein Aufruf von ausgelöst SomeType.operator==(a, b)oder, falls dies nicht vorhanden ist, ein Vergleich der Referenzgleichheit aufgerufen (was dem Aufruf entspricht object.ReferenceEquals(a, b)).

In VB hingegen ruft der Vergleich a = b immer den Gleichheitsoperator auf. 2 Wenn Sie den Referenzgleichheitsvergleich verwenden möchten, müssen Sie verwenden a Is b(was wiederum dasselbe ist wie Object.ReferenceEquals(a, b)).


1) Hier ist ein guter Hinweis darauf, warum die Verwendung Option Strict Offeine schlechte Idee ist: Ich habe VB.NET fast ein Jahrzehnt lang verwendet, von vor der offiziellen Veröffentlichung von .NET bis vor einigen Jahren, und ich habe absolut keine Ahnung, was damit zu a = btun ist Option Strict Off. Es macht eine Art Gleichheitsvergleich, aber was genau passiert und warum, keine Ahnung. Es ist jedoch komplexer als die Funktion von C # dynamic(da dies auf einer gut dokumentierten API beruht). Folgendes sagt der MSDN:

Da Option Strict Oneine starke Typisierung möglich ist , unbeabsichtigte Typkonvertierungen mit Datenverlust verhindert werden, eine späte Bindung nicht möglich ist und die Leistung verbessert wird, wird die Verwendung dringend empfohlen.

2) Jon hat eine Ausnahme erwähnt, Strings, bei denen der Gleichheitsvergleich aus Gründen der Abwärtskompatibilität einige weitere Dinge bewirkt.

Konrad Rudolph
quelle
4
+1. Ich denke, dies ist ein Fall, in dem es den Designern von VB.NET gelungen ist, die Sprache für Programmierer aus VB6 und VBA "nur funktionieren" zu lassen, wobei OOP viel weniger im Vordergrund steht und das Konzept der Referenzgleichheit daher viel weniger wichtig ist. Ein VB-Codierer kann guten Arbeitscode schreiben, ohne viel über Objekte usw. nachzudenken.
John M Gant
5
+1 Dies wird nicht so gut bewertet, wie es wirklich sollte. Nichtgebrauch Option Strict Onmuss als Straftat angesehen werden ...
Deer Hunter
1
@JohnMGant: Ein Codierer, der die Bedeutung der Referenzidentität nicht versteht, kann möglicherweise Code schreiben, der gerade funktioniert, aber es ist unwahrscheinlich, dass er wirklich weiß, welche Dinge sicher geändert werden können, welche Änderungen immer die Dinge beschädigen und welche Änderungen möglicherweise scheinen zu funktionieren, verursachen aber unerwünschte böse Nebenwirkungen (z. B. Verweise auf verschiedene veränderbare Objekte, die denselben Status haben, sollten stattdessen Verweise auf dasselbe Objekt sein). Wenn Objekte selten mutiert werden, verursacht eine solche Änderung möglicherweise keine unmittelbaren Probleme, kann jedoch dazu führen, dass später schwer zu findende Fehler auftreten.
Supercat
4

Objektinstanzen werden nicht mit dem Operator "==" verglichen. Sie sollten die Methode "equals" verwenden. Mit dem Operator "==" werden Referenzen verglichen, keine Objekte.

Versuche dies:

public class MyObject
{
    public MyObject(String v)
    {
        Value = v;
    }
    public String Value { get; set; }
}

MyObject a = new MyObject("a");
MyObject b = new MyObject("a");
if(a==b){
    Debug.WriteLine("a reference is equal to b reference");
}else{
    Debug.WriteLine("a reference is not equal to b reference");
}
if (a.Equals(b)) {
    Debug.WriteLine("a object is equal to b object");
} else {
    Debug.WriteLine("a object is not equal to b object");
}

Ergebnisse:

a reference is not equal to b reference
a object is not equal to b object

Versuchen Sie Folgendes:

public class MyObject
{
    public MyObject(String v)
    {
        Value = v;
    }
    public String Value { get; set; }

    public bool Equals(MyObject o)
    {
        return (Value.CompareTo(o.Value)==0);
    }
}
MyObject a = new MyObject("a");
MyObject b = new MyObject("a");
if(a==b){
    Debug.WriteLine("a reference is equal to b reference");
}else{
    Debug.WriteLine("a reference is not equal to b reference");
}
if (a.Equals(b)) {
    Debug.WriteLine("a object is equal to b object");
} else {
    Debug.WriteLine("a object is not equal to b object");
}

Ergebnisse:

a reference is not equal to b reference
a object is equal to b object
Lobo
quelle
1
Dies liegt einfach daran, dass Sie nicht überschrieben haben operator ==. Wenn Sie diesen Operator überschreiben und nicht gleich sind, wird Ihre Ausgabe umgekehrt. Es ist nichts inhärent, Referenzen zu vergleichen, operator ==und nichts inhärent, Werte in zu vergleichen Equals. Sie sind nur zwei Möglichkeiten, die Gleichheit zu bestimmen. Beide haben Standardimplementierungen eines Referenzvergleichs, und beide können überschrieben werden, um das zu tun, was Sie möchten. Der einzige andere Unterschied ist, dass Equalses virtuell ist und operator ==nicht.
Servy
1
@Servy: Beachten Sie, dass Sie nicht überschreiben können == - Sie können es nur überladen .
Jon Skeet
1
Entschuldigung, -1. Diese Antwort ist einfach falsch und sollte nicht die akzeptierte sein.
Konrad Rudolph
Irgendwo wartet eine Java-Frage auf diese Antwort.
Chad Schouggins
3

Das Problem ist, dass der Operator == in C # ein Aufruf einer statischen Methode ist (naja, vielleicht nicht technisch, aber es kann auch so sein), basierend auf dem Typ der Kompilierungszeit der beiden Parameter. Was die tatsächlichen Laufzeitarten dieser Objekte sind, spielt keine Rolle.

Basierend auf diesem Kompilierungszeittyp bestimmt der Compiler, welche Implementierung operator ==verwendet werden soll. Es kann die Standardimplementierung verwenden object, es kann eine der numerischen Überladungen verwenden, die von der Sprache bereitgestellt werden, oder es kann eine benutzerdefinierte Implementierung sein.

Dies unterscheidet sich von VB darin, dass VB die Implementierung zur Kompilierungszeit nicht bestimmt. Es wartet bis zur Laufzeit und überprüft die beiden angegebenen Parameter, um festzustellen, welche Implementierung des ==Operators verwendet werden soll.

Ihr Code enthält boolesche Werte, diese befinden sich jedoch in Variablen vom Typ object. Da die Variable vom Typ ist object, verwendet der C # -Compiler die objectImplementierung von ==, die die Referenzen vergleicht , nicht die Objektinstanzen. Da es sich bei den Booleschen Werten um Kästchen handelt, haben sie nicht dieselbe Referenz, obwohl ihre Werte identisch sind.

Dem VB-Code ist es egal, welcher Typ die Variable ist. Es wartet bis zur Laufzeit und überprüft dann die beiden Variablen, um festzustellen, ob sie es sind tatsächlich vom Typ Boolean sind, und verwendet daher die ==Implementierung des Booleschen Operators. Diese Implementierung vergleicht die Werte der Booleschen Werte und nicht ihre Referenzen (und die Booleschen Werte werden vor dem Aufruf dieses Operators entpackt, sodass ein Referenzvergleich nicht einmal mehr Sinn macht). Da die Werte der Booleschen Werte identisch sind, wird true zurückgegeben.

Servieren
quelle
Das sieht gut aus für das C #; Ich weiß nicht genug darüber, was genau =in VB zu sagen ist.
Jon Skeet
@ JonSkeet Fair genug.
Servy
Per msdn.microsoft.com/en-us/library/cey92b0t(v=vs.110).aspx , im Abschnitt „Ohne Typen Programmierung mit Relationale Vergleichsoperator“: =zusammen mit allen anderen relationalen Vergleichsoperator wie <, >=usw. werden speziell behandelt, wenn sich beide oder beide Seiten des Bedieners befinden Object. Diese spezielle Behandlung wird durchgeführt, damit VB6-Programmierer, die es gewohnt sind, einen Variantin pre-.NET VB bekannten Typ zu verwenden, ObjectVB.Net auf die zuvor verwendete Weise verwenden können Variant.
Rskar
Um es anders auszudrücken , und abgesehen von den Auswirkungen der Überladung und Option Strict On, ist VB darauf ausgerichtet, ein =zu entpacken, Objectbis es zu einem String oder einer Zahl kommen kann.
Rskar