Ich möchte die Szenarien verstehen, in denen IEqualityComparer<T>
und wo IEquatable<T>
sie verwendet werden sollten. Die MSDN-Dokumentation für beide sieht sehr ähnlich aus.
151
Ich möchte die Szenarien verstehen, in denen IEqualityComparer<T>
und wo IEquatable<T>
sie verwendet werden sollten. Die MSDN-Dokumentation für beide sieht sehr ähnlich aus.
EqualityComparer<T>
, die Schnittstelle zu erben, anstatt sie zu implementieren, "weil dieEqualityComparer<T>
Gleichheit mitIEquatable<T>
T
Implementierung eine benutzerdefinierte Sammlung erstellen sollteIEquatable<T>
. Hätte eine SammlungList<T>
sonst gerne einen subtilen Fehler?Antworten:
IEqualityComparer<T>
ist eine Schnittstelle für ein Objekt, die den Vergleich für zwei Objekte des Typs durchführtT
.IEquatable<T>
ist für ein Objekt vom Typ,T
damit es sich mit einem anderen Objekt des gleichen Typs vergleichen kann.quelle
IEqualityComparer<T>
ist eine Schnittstelle für ein Objekt (die in der Regel ist eine leichte Klasse unterschiedlichT
) , die Vergleichsfunktionen bereitstellt , die auf betreibtT
Bei der Entscheidung, ob
IEquatable<T>
oder verwendet werden sollIEqualityComparer<T>
, könnte man fragen:Wenn es nur eine Möglichkeit gibt, zwei Instanzen
T
auf Gleichheit zu testen , oder wenn eine von mehreren Methoden bevorzugt wird, istIEquatable<T>
dies die richtige Wahl: Diese Schnittstelle sollte nur für sichT
selbst implementiert werden , damit eine InstanzT
über internes Wissen verfügt wie man sich mit einer anderen Instanz von vergleichtT
.Auf der anderen Seite erscheint es angemessener, wenn es mehrere gleichermaßen vernünftige Methoden zum Vergleichen von zwei
T
s auf Gleichheit gibtIEqualityComparer<T>
: Diese Schnittstelle soll nicht für sich selbst implementiert werdenT
, sondern für andere "externe" Klassen. Wenn Sie zwei InstanzenT
auf Gleichheit testenT
, müssen Sie daher eine explizite Auswahl einerIEqualityComparer<T>
Instanz treffen, die den Test gemäß Ihren spezifischen Anforderungen ausführt , da kein internes Verständnis der Gleichheit vorliegt.Beispiel:
Betrachten wir diese beiden Typen (die angeblich haben Wertesemantik ):
Warum sollte nur einer dieser Typen erben
IEquatable<>
, der andere nicht?Theoretisch gibt es nur einen sinnvollen Weg, zwei Instanzen beider Typen zu vergleichen: Sie sind gleich, wenn die Eigenschaften
X
undY
in beiden Instanzen gleich sind. Nach dieser Überlegung sollten beide Typen implementiert werdenIEquatable<>
, da es nicht wahrscheinlich ist, dass es andere sinnvolle Möglichkeiten gibt, einen Gleichheitstest durchzuführen.Das Problem hierbei ist, dass der Vergleich von Gleitkommazahlen auf Gleichheit aufgrund winziger Rundungsfehler möglicherweise nicht wie erwartet funktioniert . Es gibt verschiedene Methoden zum Vergleichen von Gleitkommazahlen auf nahezu Gleichheit mit jeweils spezifischen Vorteilen und Kompromissen. Möglicherweise möchten Sie selbst auswählen können, welche Methode geeignet ist.
Beachten Sie, dass auf der Seite, auf die ich (oben) verwiesen habe, ausdrücklich angegeben ist, dass dieser Test auf nahezu Gleichheit einige Schwächen aufweist. Da dies eine
IEqualityComparer<T>
Implementierung ist, können Sie sie einfach austauschen, wenn sie für Ihre Zwecke nicht gut genug ist.quelle
IEqualityComparer<T>
Implementierung eine Äquivalenzbeziehung implementieren muss , was bedeutet, dass in allen Fällen, in denen zwei Objekte gleich einem Drittel sind, sie gleich miteinander verglichen werden müssen . Jede Klasse, die das oben Gesagte implementiertIEquatable<T>
oderIEqualityComparer<T>
auf eine Weise widerspricht, ist gebrochen.IEqualityComparer<String>
dem "Hallo" gleich "Hallo" und "hElLo" ist, "Hallo" und "hElLo" gleich betrachten muss, aber für die meisten Vergleichsmethoden wäre dies kein Problem.GetHashCode
sollten Sie schnell feststellen können, ob sich zwei Werte unterscheiden. Die Regeln lauten ungefähr wie folgt: (1)GetHashCode
muss immer den gleichen Hashcode für den gleichen Wert erzeugen. (2)GetHashCode
sollte schnell sein (schneller alsEquals
). (3)GetHashCode
muss nicht präzise sein (nicht so präzise wieEquals
). Dies bedeutet, dass möglicherweise der gleiche Hashcode für verschiedene Werte erzeugt wird. Je präziser Sie es machen können, desto besser, aber es ist wahrscheinlich wichtiger, es schnell zu halten.Sie haben bereits die grundlegende Definition dessen, was sie sind . Kurz gesagt, wenn Sie eine
IEquatable<T>
Klasse implementierenT
, gibt dieEquals
Methode für ein Objekt vom Typ anT
, ob das Objekt selbst (das auf Gleichheit getestete) einer anderen Instanz desselben Typs entsprichtT
. DientIEqualityComparer<T>
zum Testen der Gleichheit von zwei beliebigen Instanzen vonT
, typischerweise außerhalb des Geltungsbereichs der Instanzen vonT
.In Bezug auf was sie sind für verwirrend auf den ersten sein. Aus der Definition sollte klar sein, dass daher
IEquatable<T>
(in der KlasseT
selbst definiert) der De-facto-Standard sein sollte, um die Eindeutigkeit seiner Objekte / Instanzen darzustellen.HashSet<T>
,Dictionary<T, U>
(unter BerücksichtigungGetHashCode
wird auch überschrieben),Contains
aufList<T>
etc nutzen Sie dies. Das ImplementierenIEqualityComparer<T>
aufT
hilft den oben genannten allgemeinen Fällen nicht. In der Folge gibt es wenig Wert für die ImplementierungIEquatable<T>
in einer anderen Klasse alsT
. Dies:macht selten Sinn.
Andererseits
ist, wie es gemacht werden sollte.
IEqualityComparer<T>
kann nützlich sein, wenn Sie eine benutzerdefinierte Validierung der Gleichheit benötigen, jedoch nicht in der Regel. Zum Beispiel müssen Sie in einer Klasse vonPerson
irgendwann die Gleichheit von zwei Personen anhand ihres Alters testen. In diesem Fall können Sie Folgendes tun:Versuchen Sie es, um sie zu testen
Ebenso macht
IEqualityComparer<T>
onT
keinen Sinn.Das funktioniert zwar, sieht aber für die Augen nicht gut aus und besiegt die Logik.
Normalerweise brauchen Sie
IEquatable<T>
. Idealerweise können Sie auch nur eine haben,IEquatable<T>
während mehrereIEqualityComparer<T>
nach verschiedenen Kriterien möglich sind.Die
IEqualityComparer<T>
undIEquatable<T>
sind genau analog zuComparer<T>
undIComparable<T>
werden zu Vergleichszwecken verwendet, anstatt sie gleichzusetzen. Ein guter Thread hier, wo ich die gleiche Antwort geschrieben habe :)quelle
public int GetHashCode(Person obj)
sollte zurückkehrenobj.GetHashCode()
obj.Age.GetHashCode
. wird bearbeiten.person.GetHashCode
nirgendwo direkt angerufen ... warum sollten Sie das überschreiben? - Wir überschreiben, weilIEqualityComparer
es darum geht, eine andere Vergleichsimplementierung gemäß unseren Regeln durchzuführen - den Regeln, die wir wirklich gut kennen.Age
Eigenschaft stützen , also rufe ich anAge.GetHashCode
. Das Alter ist vom Typint
, aber was auch immer der Typ ist, der Hash-Code-Vertrag in .NET ist dasdifferent objects can have equal hash but equal objects cant ever have different hashes
. Dies ist ein Vertrag, an den wir blind glauben können. Natürlich sollten auch unsere benutzerdefinierten Vergleicher diese Regel einhalten. Wenn jemals ein AufrufsomeObject.GetHashCode
kaputt geht, dann ist es das Problem mit Implementierern vom TypsomeObject
, es ist nicht unser Problem.IEqualityComparer wird verwendet, wenn die Gleichheit zweier Objekte extern implementiert wird, z. B. wenn Sie einen Vergleicher für zwei Typen definieren möchten, für die Sie keine Quelle hatten, oder wenn die Gleichheit zweier Dinge nur in einem begrenzten Kontext sinnvoll ist.
IEquatable ist für das Objekt selbst (das Objekt, das auf Gleichheit verglichen wird) zu implementieren.
quelle
Man vergleicht zwei
T
s. Der andere kann sich mit anderen vergleichenT
. Normalerweise müssen Sie jeweils nur einen verwenden, nicht beide.quelle