Wie Sie feststellen, wenn zwei generische Typ-Werte gleich sind?
Update*
Ich bin so traurig... mein Beispiel-code enthielt einen Fehler, in dessen Folge viele Antworten habe ich nicht verstanden.
Statt
Console.WriteLine("3. this.Equals " + (go1.Equals(go2)));
Ich soll schreiben
Console.WriteLine("3. this.Equals " + (go1.Equals(sb2)));
Ich versuche, herauszufinden, wie kann ich erfolgreich bestimmen, wenn zwei generische Typ-Werte sind einander gleich. Basierend auf Mark Byers' Antwort auf diese Frage ich würde denken, kann ich einfach value.Equals()
Wert ist, wobei ein generischer Typ.
Mein eigentliche problem ist in einer LinkedList-Implementierung, aber das problem kann angezeigt werden mit diesem einfacheren Beispiel.
class GenericOjbect<T> {
public T Value { get; private set; }
public GenericOjbect(T value) {
Value = value;
}
public bool Equals(T value) {
return (Value.Equals(value));
}
}
Nun definiere ich eine Instanz von GenericObject<StringBuilder>
mit new StringBuilder("StackOverflow")
. Ich würde erwarten, dass man true
wenn ich den Anruf Equals(new StringBuilder("StackOverflow")
auf dieser GenericObject Instanz, aber ich bekomme false
.
Einem Beispiel-Programm zeigt dies:
using System;
using System.Text;
class Program
{
static void Main()
{
var sb1 = new StringBuilder("StackOverflow");
var sb2 = new StringBuilder("StackOverflow");
Console.WriteLine("StringBuilder compare");
Console.WriteLine("1. == " + (sb1 == sb2));
Console.WriteLine("2. Object.Equals " + (Object.Equals(sb1, sb2)));
Console.WriteLine("3. this.Equals " + (sb1.Equals(sb2)));
var go1 = new GenericOjbect<StringBuilder>(sb1);
var go2 = new GenericOjbect<StringBuilder>(sb2);
Console.WriteLine("\nGenericObject compare");
Console.WriteLine("1. == " + (go1 == go2));
Console.WriteLine("2. Object.Equals " + (Object.Equals(go1, sb2)));
Console.WriteLine("3. this.Equals " + (go1.Equals(sb2)));
Console.WriteLine("4. Value.Equals " + (go1.Value.Equals(sb2.Value)));
}
}
Für die drei Methoden zum Vergleich von zwei StringBuilder-Objekte, nur die StringBuilder.Gleich-Instanz-Methode (Dritte Zeile) gibt true
. Dies ist, was ich erwartet hatte. Aber wenn man die GenericObject Objekte, deren Equals () - Methode (die Dritte Zeile) gibt false
. Interessanterweise ist der vierte Methode vergleichen hat zurück true
. Ich denke, die Dritte und vierte Vergleich sind eigentlich die gleiche Sache zu tun.
Hätte ich erwartet true
. Denn in der Equals () - Methode der Klasse GenericObject, beide value
und Value
Typ T
in diesem Fall ist eine StringBuilder
. Basierend auf Mark Byers' Antwort in diese Frage, ich hätte gedacht, dass die Value.Equals()
Methode zu verwenden, die das StringBuilder-Objekt die Equals () - Methode. Und wie ich gezeigt habe, das StringBuilder-Objekt Gleich ist () - Methode zurück true
.
Habe ich sogar versucht
public bool Equals(T value) {
return EqualityComparer<T>.Default.Equals(Value, value);
}
aber, dass auch false zurück.
So, zwei Fragen hier:
- Warum nicht den code zurück
true
? - Wie könnte ich implementieren, die
Equals
Methode, so dass es hat zurücktrue
?
Du musst angemeldet sein, um einen Kommentar abzugeben.
Wie vorgeschlagen in Marc Gravell ' s Antwort, das problem ist mit
StringBuilder
Equals(object)
Implementierung, die im Unterschied zu der inEquals(StringBuilder)
.Dann können Sie das problem ignorieren, weil Ihr code funktioniert mit jedem anderen in sich schlüssig-implementierte Klassen verwenden, oder Sie können dynamisch um das problem zu beheben (wieder-so der Vorschlag von Mark Gravell).
Aber angesichts der Tatsache, dass Sie nicht mit C# 4 (also nicht dynamisch), können Sie versuchen, auf diese Weise:
true
. Können Sie vorschlagen, einen anderen Typ der Referenz-Klasse in der .NET Framework, die kann ich einfach überprüfen, meineEquals
Umsetzung?Equals(type of class itself)
sowieEquals(Object)
um als "kohärent implementiert"?Equals(object)
genannt wird. Also, wenn Sie bei der Umsetzung Ihrer benutzerdefiniertenEquals(type of class itself)
warum sollten Sie nicht überschreiben die Standard-auch eins? Auch ist es eine Zeile code:return this.Equals(obj as TypeOftheClass);
. Dies ist eine kohärentere IMO.LinkedList.Find()
verwendetEqualityComparer<T>.Default
(habe ich überprüft, die mittels red gate ' Reflektor demontieren Sie den code)Dein code sieht gut aus. Das problem hier ist, dass die Klasse StringBuilder hat eine unübersichtliche Menge von gleich zu Gleich, die widersprüchlich sind. Insbesondere Equals ("StringBuilder") ist nicht einverstanden mit Equals(Objekt), selbst wenn das Objekt ist einen StringBuilder.
Alle, die
EqualityComparer<T>
muss ist eine vernünftige Equals(object) Implementierung. Die Schnittstelle (IEquatable<T>
) ist optional. Leider StringBuilder nicht über diese (zumindest im Vergleich zu den "Equals" ("StringBuilder"), die Ihre Dritte test verwendet).Aber im Allgemeinen, der Rat ist: verwenden Sie
EqualityComparer<T>
; dies unterstützt:Equals(T v)
stattEquals(GenericObject<T> v)
ist das problem.return (value.Equals(Value))
in meine Equals-Methode ruft die StringBuilder-Objekt die Equals(object) - Methode? Wenn ja, warum? Ist es, weil ein generischer Typ wie T ist eigentlich als ein Objekt?GenericObject<T>
, aber zu einem anderenT
. Wenn ich will, um vergleichen zuGenericObject<T>
Instanzen, ich würde nicht nur vergleichen, wenn beide auf das gleiche Objekt zeigen? In der TatEquals(GenericObject<T> genericObject)
gar nicht kompilieren.dynamic
nützlich sein kann; es verwendet die am besten geeignete Methode zur Verfügung, Laufzeit für jeden einzelnen T.public bool Equals(GenericObject<T> genericObject)
erhalte ich die Fehlermeldung "Der Typ oder namespace-name 'GenericObject" konnte nicht gefunden werden".GenericOjbect
stattGenericObject
😉GenericObject<T>
nurT
.dynamic
als ich mit .NET 3.5 hier (und VS2008)DateTime
, aber das ist eine Struktur, in der Stelle von der a-Klasse. Der nächste Typ, der mir einfiel, war ein StringBuilder-Objekt.string
als ob es ein Wert-Typ. Das ist genau der Grund, warum ich versuchte, eine andere zu verwenden .NET Art, eine, die tatsächlich ist ein Referenz-Typ.Equals
,CompareTo
,==
,!=
etc.Linie 3 mit dem generischen Objekt nicht aufrufen der benutzerdefinierten Methode geschrieben. Stattdessen ruft der Basis
Object.Equals(object)
. Rufen Sie Ihre eigene Methode, du musst in einerT
keinGenericObject<T>
. So etwas wie:go1.Equals(go2.Value)
Object.Equals(object)
. Jedoch, mitgo1.Equals(go2.Value)
macht keinen Unterschied. Auch, was ich schreiben möchte, hier ist eine Equals-Methode auf GenericObject, die in der Lage ist, zum vergleichen von zwei GenericObject Instanzen.Equals
MethodeMyEquals
es funktioniert nicht. Die 3. Zeile jetzt nicht nennen, meine MyEquals Methode. Aber seinereturn (value.Equals(Value)
noch zurückfalse
.go1.Equals(go2)
abergo1.Equals(sb2)
. So jetzt habe ich do übergeben Sie eineT
, und dieEquals
aufgerufen wird. Jedoch, es noch zurückfalse
. Ich aktualisiert meine Frage.Als Eric Lippert sagt, dass in Antwort auf diese Frage - Überlast Auflösung erfolgt zur compile-Zeit.
Wenn man einen Blick auf
StringBuilder
's Implementierung Sie werden feststellen, es überlastungenEquals
und nicht überschrieben. Dies ist im Grunde die Wurzel des Problems, warumStringBuilder.Equals
funktioniert nicht wie erwartet in Ihrem Beispiel.Nehmen Sie die folgenden 2 Klassen als Beispiel.
Overloader
ist Analog zuStringBuilder
im Beispiel, wie es überlastungenEquals
.Overrider
ist sehr ähnlich, außer er überschreibt esEquals
statt.Habe ich leicht modifiziert Ihre
GenericObject<T>
Klasse in meinem Beispiel:In diesem Beispielprogramm sehen Sie, dass
Overloader
(oderStringbuilder
für diese Angelegenheit) den Wert false zurück. AllerdingsOverrider
true zurück.Verweisen auf Eric Lippert wieder - Überlast Auflösung erfolgt zur compile-Zeit. Was bedeutet, dass der compiler im Grunde schaut auf Eure
GenericObject<T>.Equals( T val )
wie diese:Anwser zu Ihrer Frage , Wie um zu bestimmen, wenn zwei generische Typ-Werte gleich sind?. Es gibt zwei Dinge, die Sie möglicherweise tun könnte.
GenericObject<T>
sicherzustellen, dass Sie alle mindestens überschreibenEquals
.GenericObject<T>.Equals(T val)
manuell führen Sie die späte Bindung.Vergleichen Sie die Ausgabe von typeof() den ersten, so stellen Sie sicher Sie vergleichen die gleiche Art von Objekten, dann schreiben Sie eine Equals-Methode auf X-Klasse, die nimmt eine andere Instanz X der Klasse und vergleichen Sie alle Eigenschaften... sobald Sie etwas finden, was anders ist, false zurück, sonst halten Sie weiter, bis Sie true zurück.
Prost 🙂
Können Sie entweder implementieren
, IEquatable<T>
, oder implementieren Sie einen comparer-Klasse, die, IEqualityComparer<T>
.Stellen Sie sicher, dass
value
Sie überprüfen die Gleichheit ist unveränderlich und wird nur bei der Initialisierung der Klasse.Andere überlegung wäre die Umsetzung
IComparer<T>
, wenn Sie implementieren diese ein, werden Sie nicht haben, um sorgen über den hash-code, und damit umgesetzt werden kann, für veränderliche Typen/Felder als gut.Sobald Sie richtig implementieren
, IEquatable<T>
in Ihrer Klasse, Ihre Fragen werden gelöst.Update:
Aufruf
return EqualityComparer<T>.Default.Equals(Value, value);
würde grundsätzlich return gleichen Ergebnis, da es keineIEqualityComparer<T>
umgesetzt...IEquatable<T>.Equals
alsreturn (value.Equals(Value));
und erhalten die gleiche (falsche) Ergebnis.value
werden, unveränderlich sind?Zu erarbeiten, Gideon ' s Antwort (bitte upvote seine, nicht von mir): die Methode, die Sie definiert hat Unterschrift
Während Ihr code ruft
denen geerbt wird (nicht überschrieben) aus
Object
.go1.Equals(go2)
wo ich sagen wolltego1.Equals(sb2)
. Also ich do haben möchten, eineEquals(T value)
. Meine Klasse zeigte meine Absicht, mein test-Programm verursacht eine Menge Verwirrung. Ich aktualisiert meine Frage.