Gemäß der Dokumentation des ==
Betreibers in MSDN ,
Für vordefinierte Werttypen gibt der Gleichheitsoperator (==) true zurück, wenn die Werte seiner Operanden gleich sind, andernfalls false. Für andere Referenztypen als Zeichenfolge gibt == true zurück, wenn sich die beiden Operanden auf dasselbe Objekt beziehen. Für den Zeichenfolgentyp vergleicht == die Werte der Zeichenfolgen. Benutzerdefinierte Werttypen können den Operator == überladen (siehe Operator). Dies gilt auch für benutzerdefinierte Referenztypen, obwohl sich == standardmäßig wie oben für vordefinierte und benutzerdefinierte Referenztypen beschrieben verhält.
Warum kann dieses Code-Snippet nicht kompiliert werden?
bool Compare<T>(T x, T y) { return x == y; }
Ich erhalte den Fehler Operator '==' kann nicht auf Operanden vom Typ 'T' und 'T' angewendet werden . Ich frage mich warum, da der ==
Operator meines Wissens für alle Typen vordefiniert ist.
Edit: Danke an alle. Ich habe zunächst nicht bemerkt, dass es sich bei der Aussage nur um Referenztypen handelt. Ich dachte auch, dass ein bitweiser Vergleich für alle Werttypen bereitgestellt wird, von denen ich jetzt weiß, dass sie nicht korrekt sind.
==
Wenn ich jedoch einen Referenztyp verwende, würde der Operator den vordefinierten Referenzvergleich verwenden oder würde er die überladene Version des Operators verwenden, wenn ein Typ einen definiert?
Bearbeiten 2: Durch Versuch und Irrtum haben wir erfahren, dass der ==
Operator den vordefinierten Referenzvergleich verwendet, wenn er einen uneingeschränkten generischen Typ verwendet. Tatsächlich verwendet der Compiler die beste Methode, die er für das Argument des eingeschränkten Typs finden kann, sucht jedoch nicht weiter. Der folgende Code wird beispielsweise immer gedruckt true
, auch wenn er Test.test<B>(new B(), new B())
aufgerufen wird:
class A { public static bool operator==(A x, A y) { return true; } }
class B : A { public static bool operator==(B x, B y) { return false; } }
class Test { void test<T>(T a, T b) where T : A { Console.WriteLine(a == b); } }
quelle
==
zwischen zwei Operanden desselben Typs nicht zulässig ist. Dies gilt fürstruct
Typen (außer "vordefinierten" Typen), die das nicht überladenoperator ==
. Versuchen Sie als einfaches Beispiel Folgendes:var map = typeof(string).GetInterfaceMap(typeof(ICloneable)); Console.WriteLine(map == map); /* compile-time error */
var kvp1 = new KeyValuePair<int, int>(); var kvp2 = kvp1;
, dann können Sie nicht überprüfen,kvp1 == kvp2
daKeyValuePair<,>
es sich um eine Struktur handelt, es sich nicht um einen vordefinierten C # -Typ handelt und der nicht überladen wirdoperator ==
. Es wird jedoch ein Beispiel gegeben,var li = new List<int>(); var e1 = li.GetEnumerator(); var e2 = e1;
mit dem Sie nicht arbeiten könnene1 == e2
(hier haben wir die verschachtelte StrukturList<>.Enumerator
("List`1+Enumerator[T]"
von der Laufzeit aufgerufen ), die nicht überladen wird==
).bool
von einemvoid
...Antworten:
"... verhält sich == standardmäßig wie oben beschrieben für vordefinierte und benutzerdefinierte Referenztypen."
Typ T ist nicht unbedingt ein Referenztyp, daher kann der Compiler diese Annahme nicht treffen.
Dies wird jedoch kompiliert, da es expliziter ist:
Folgen Sie der zusätzlichen Frage: "Wenn ich jedoch einen Referenztyp verwende, würde der Operator == den vordefinierten Referenzvergleich verwenden oder würde er die überladene Version des Operators verwenden, wenn ein Typ einen definiert?"
Ich hätte gedacht, dass == auf den Generika die überladene Version verwenden würde, aber der folgende Test zeigt etwas anderes. Interessant ... Ich würde gerne wissen warum! Wenn jemand weiß, bitte teilen.
Ausgabe
Inline: Überladen == aufgerufen
Generisch:
Drücken Sie eine beliebige Taste, um fortzufahren . . .
Follow Up 2
Ich möchte darauf hinweisen, dass ich meine Vergleichsmethode auf ändere
bewirkt, dass der überladene Operator == aufgerufen wird. Ich denke, ohne Angabe des Typs (als Wo ) kann der Compiler nicht schließen, dass er den überladenen Operator verwenden sollte ... obwohl ich denke, dass er genügend Informationen hätte, um diese Entscheidung zu treffen, auch ohne Angabe des Typs.
quelle
Equals
Methode durchführen sollten (nicht im==
Operator).==
zwischen generischen TypenT
und habenT
, wird die beste Überladung gefunden, wenn man bedenkt, von welchen Einschränkungen sie getragen werdenT
(es gibt eine spezielle Regel, dass niemals ein Werttyp dafür eingefügt wird (was zu einem bedeutungslosen Ergebnis führen würde), daher muss es eine geben einige Einschränkungen, die garantieren, dass es sich um einen Referenztyp handelt). Wenn Sie in Ihrem Follow-up 2 mitDerivedTest
Objekten hereinkommen undDerivedTest
vonTest
einer neuen Überladung von stammen==
, diese aber einführen , haben Sie erneut das "Problem". Welche Überladung aufgerufen wird, wird zur Kompilierungszeit in die IL "eingebrannt".Wie andere gesagt haben, funktioniert es nur, wenn T auf einen Referenztyp beschränkt ist. Ohne Einschränkungen können Sie mit null vergleichen, aber nur mit null - und dieser Vergleich ist für nicht nullfähige Werttypen immer falsch.
Anstatt Equals aufzurufen, ist es besser, ein zu verwenden
IComparer<T>
- und wenn Sie keine weiteren Informationen haben,EqualityComparer<T>.Default
ist dies eine gute Wahl:Abgesehen von allem anderen vermeidet dies Boxen / Casting.
quelle
Im Allgemeinen
EqualityComparer<T>.Default.Equals
sollte die Arbeit mit allem erledigt werdenIEquatable<T>
, was implementiert wird oder was eine vernünftigeEquals
Implementierung hat.Wenn jedoch
==
undEquals
anders aus irgendwelchen Gründen umgesetzt werden, dann meine Arbeit an generischen Operationen sollte nützlich sein; Es unterstützt unter anderem die Operator- Versionen von:quelle
So viele Antworten und keine einzige erklärt das WARUM? (was Giovanni ausdrücklich fragte) ...
.NET-Generika verhalten sich nicht wie C ++ - Vorlagen. In C ++ - Vorlagen tritt eine Überlastungsauflösung auf, nachdem die tatsächlichen Vorlagenparameter bekannt sind.
In .NET-Generika (einschließlich C #) tritt eine Überlastungsauflösung auf, ohne die tatsächlichen generischen Parameter zu kennen. Die einzigen Informationen, die der Compiler zur Auswahl der aufzurufenden Funktion verwenden kann, stammen aus Typbeschränkungen für die generischen Parameter.
quelle
==
funktioniert es für alle Typen, sei es Referenztypen oder Werttypen. Das sollte die Frage sein, auf die Sie meiner Meinung nach nicht geantwortet haben.==
funktioniert nicht für alle Werttypen . Noch wichtiger ist, dass es nicht für alle Typen die gleiche Bedeutung hat, sodass der Compiler nicht weiß, was er damit machen soll.==
. Können Sie diesen Teil auch in Ihre Antwort aufnehmen, da ich denke, dass dies der Hauptpunkt hier istDie Kompilierung kann nicht wissen, dass T keine Struktur (Werttyp) sein kann. Sie müssen also sagen, dass es nur vom Referenztyp sein kann, denke ich:
Dies liegt daran, dass wenn T ein Werttyp sein könnte, es Fälle geben könnte, in denen
x == y
eine Fehlbildung vorliegt - in Fällen, in denen für einen Typ kein Operator == definiert ist. Dasselbe wird dafür passieren, was offensichtlicher ist:Das schlägt auch fehl, weil Sie einen Typ T übergeben könnten, der keine Funktion foo hätte. C # zwingt Sie sicherzustellen, dass alle möglichen Typen immer eine Funktion foo haben. Dies geschieht durch die where-Klausel.
quelle
Es scheint, dass ohne die Klassenbeschränkung:
Man sollte sich darüber im Klaren sein, dass während eine
class
EinschränkungEquals
im==
Operator von erbtObject.Equals
, während die einer Struktur überschreibtValueType.Equals
.Beachten Sie, dass:
gibt auch den gleichen Compilerfehler aus.
Bis jetzt verstehe ich nicht, warum ein Vergleich des Werttyp-Gleichheitsoperators vom Compiler abgelehnt wird. Ich weiß jedoch, dass dies funktioniert:
quelle
Object.Equals
sondern prüft Referenz Gleichheit. Zum BeispielCompare("0", 0.ToString())
würde false zurückgegeben, da die Argumente Verweise auf unterschiedliche Zeichenfolgen sind, die beide eine Null als einziges Zeichen haben.NullReferenceException
könnte ein Ereignis auftreten .Nun, in meinem Fall wollte ich den Gleichheitsoperator einem Unit-Test unterziehen. Ich musste den Code unter den Gleichheitsoperatoren aufrufen, ohne den generischen Typ explizit festzulegen. Ratschläge für
EqualityComparer
waren alsEqualityComparer
aufgerufeneEquals
Methode nicht hilfreich , aber nicht als Gleichheitsoperator.Hier ist, wie ich das mit generischen Typen arbeiten lasse, indem ich ein baue
LINQ
. Es ruft den richtigen Code für==
und!=
Operatoren auf:quelle
Es gibt eine MSDN Connect - Eintrag für diese hier
Die Antwort von Alex Turner beginnt mit:
quelle
Wenn Sie sicherstellen möchten, dass die Operatoren Ihres benutzerdefinierten Typs aufgerufen werden, können Sie dies über Reflection tun. Rufen Sie einfach den Typ mit Ihrem generischen Parameter ab und rufen Sie die MethodInfo für den gewünschten Operator ab (z. B. op_Equality, op_Inequality, op_LessThan ...).
Führen Sie dann den Operator mit der Invoke-Methode von MethodInfo aus und übergeben Sie die Objekte als Parameter.
Dadurch wird Ihr überladener Operator aufgerufen und nicht derjenige, der durch die auf den generischen Parameter angewendeten Einschränkungen definiert ist. Dies ist möglicherweise nicht praktikabel, kann sich jedoch als nützlich für Unit-Tests Ihrer Bediener erweisen, wenn Sie eine generische Basisklasse verwenden, die einige Tests enthält.
quelle
Ich habe die folgende Funktion geschrieben und mir die neueste msdn angesehen. Es kann leicht zwei Objekte vergleichen
x
undy
:quelle
return ((IComparable)(x)).CompareTo(y) <= 0;
Dies funktioniert, da == bei benutzerdefinierten Referenztypen berücksichtigt wird.
Bei Werttypen kann == überschrieben werden. In diesem Fall sollte auch "! =" Definiert werden.
Ich denke, das könnte der Grund sein, es erlaubt keinen generischen Vergleich mit "==".
quelle
==
Token wird für zwei verschiedene Operatoren verwendet. Wenn für die angegebenen Operandentypen eine kompatible Überladung des Gleichheitsoperators vorliegt, wird diese Überladung verwendet. Andernfalls wird ein Referenzvergleich verwendet, wenn beide Operanden Referenztypen sind, die miteinander kompatibel sind. Beachten Sie, dassCompare
der Compiler in der obigen Methode nicht erkennen kann, dass die erste Bedeutung gilt, aber die zweite Bedeutung zutreffen kann, sodass das==
Token diese auch dann verwendet, wennT
der Operator für die Gleichheitsprüfung überlastet wird (z. B. wenn es sich um einen Typ handeltString
) .Das
.Equals()
funktioniert bei mir zwarTKey
ein generischer Typ.quelle
x.Id.Equals
nicht soid.Equals
. Vermutlich weiß der Compiler etwas über den Typ vonx
.