Angenommen, ich möchte 2 Listen von Ints vergleichen und einen bestimmten Wert als Joker behandeln.
z.B. Wenn -1 ein Platzhalter ist, dann
{1,2,3,4} == {1,2,-1,4} //returns true
Und ich schreibe eine Klasse, die all diese Logik umschließt, also implementiert sie IEquatable
und hat die relevante Logik in public override bool Equals()
Aber ich dachte immer, dass Sie mehr oder weniger GetHashCode
implementieren müssen, wenn Sie .Equals()
außer Kraft gesetzt haben. Zugegeben, es wird nicht vom Compiler erzwungen, aber ich habe den Eindruck, dass wenn Sie das nicht tun, Sie es falsch machen.
Außer ich sehe nicht, wie ich .GetHashCode()
implementieren kann, ohne entweder seinen Vertrag zu brechen (Objekte, die gleich sind, verschiedene Hashes haben) oder einfach die Implementierung return 1
zu haben.
Gedanken?
Diese Implementierung von Equals
ist bereits ungültig, da sie nicht transitiv ist. Sie sollten Equals
wahrscheinlich mit der Standardimplementierung belassen und eine neue Methode wie WildcardEquals
schreiben (wie in den anderen Antworten hier vorgeschlagen).
Wenn Sie Equals
geändert haben, müssen Sie im Allgemeinen GetHashCode
implementieren, wenn Sie die Objekte in einer Hashtabelle speichern möchten (z. B. Dictionary<TKey, TValue>
), damit sie ordnungsgemäß funktionieren. Wenn Sie sicher sind, dass die Objekte niemals in einer Hashtabelle enden werden, ist dies theoretisch optional (aber es wäre sicherer und klarer in diesem Fall, sie zu überschreiben, um ein " NotSupportedException
" zu werfen oder immer 0
zurückzugeben. ).
Der allgemeine Vertrag besteht darin, immer GetHashCode
zu implementieren, wenn Sie Equals
überschreiben, da Sie nicht immer sicher sein können, dass spätere Benutzer Ihre Objekte nicht in Hashtabellen einfügen.
Von einem logischen Standpunkt aus brechen wir das Konzept der Gleichheit. Es ist nicht mehr transitiv. Im Fall von Platzhaltern bedeutet A==B
und B==C
nicht, dass A==C
.
Aus technischer Sicht ist es nicht unverzeihlich, den gleichen Wert aus GetHashCode()
zurückzugeben.
GetHashCode
ist im Allgemeinen nur dann wichtig, wenn Sie Elemente Ihrer Klasse in einer Art von Sammlung speichern möchten, beispielsweise in einem Set. Wenn das der Fall ist, dann glaube ich nicht, dass Sie eine konsistente Semantik erreichen können, da @AlexD darauf hinweist, dass die Gleichheit nicht mehr transitiv ist.
Wenn Sie z. B. String-Globs anstelle von Integer-Listen verwenden und die Strings "A", "B" und "*" zu einem Set hinzufügen, wird Ihr Set je nach dem entweder ein oder zwei Elemente enthalten Reihenfolge, die Sie hinzufügen.
Wenn Sie das nicht möchten, empfehle ich, den Platzhalter in eine neue Methode zu übernehmen (z. B. EquivalentTo()
) anstatt die Gleichheit zu überladen.
Wenn GetHashCode () immer eine Konstante zurückgibt, ist dies die einzige "legale" Art, die Bedingung "equals / hashcode" zu erfüllen.
Es wird möglicherweise ineffizient sein, wenn Sie es in eine Hashmappe oder Ähnliches schreiben, aber das mag in Ordnung sein (ungleiche Hashcodes implizieren Nichtgleichheit, aber gleiche Hashcodes implizieren nichts).
Ich denke, das ist die einzig mögliche Option dort. Hashcodes gibt es im Wesentlichen als Schlüssel, um schnell nach Dingen zu suchen, und da Ihr Platzhalter jedem Objekt entsprechen muss, muss sein Schlüssel für die Suche dem Schlüssel jedes Elements entsprechen, also müssen alle gleich sein.
Wie andere bemerkt haben, ist dies nicht das, wofür normalerweise equals ist, und bricht Annahmen, die viele andere Dinge für equals verwenden können (wie Transitivität - EDIT: stellt sich heraus, dass dies eigentlich vertragliche Voraussetzung ist, also no-go) Es lohnt sich also zumindest, diese manuell oder mit einem explizit getrennten Gleichheitsvergleich zu vergleichen.
Da Sie geändert haben, was "gleich" bedeutet (das Hinzufügen von Platzhaltern ändert die Dinge dramatisch), liegen Sie bereits außerhalb des normalen Anwendungsbereichs von Equals und GetHashCode. Es ist nur eine Empfehlung und in Ihrem Fall scheint es, dass es nicht passt. Also mach dir keine Sorgen.
Stellen Sie sicher, dass Sie Ihre Klasse nicht an Orten verwenden, an denen GetHashCode verwendet werden kann. Das kann Sie in eine Menge Probleme bringen und schwer zu debuggen sein, wenn Sie nicht darauf achten.
Es wird allgemein erwartet, dass Equals(Object)
und IEquatable<T>.Equals(T)
Äquivalenzrelationen implementieren sollten, so dass, wenn beobachtet wird, dass X gleich Y ist und dass Y gleich Z ist, und keines der Elemente geändert wurde , X kann als gleich Z angenommen werden; zusätzlich, wenn X gleich Y ist und Y nicht gleich Z ist, dann kann angenommen werden, dass X auch nicht gleich Z ist. Wildcard- und Fuzzy-Vergleichsmethoden implementieren keine Äquivalenzrelationen und daher sollte Equals
generell nicht mit solchen Semantiken implementiert werden.
Viele Auflistungen arbeiten irgendwie mit Objekten, die Equals
implementieren, auf eine Weise, die keine Äquivalenzbeziehung implementiert, vorausgesetzt, dass zwei Objekte, die möglicherweise miteinander vergleichbar sind, immer den gleichen Hash-Code zurückgeben. Dies erfordert oft, dass viele Dinge, die ungleich vergleichen würden, den gleichen Hash-Code zurückgeben, obwohl abhängig davon, welche Arten von Platzhaltern unterstützt werden, es möglich ist, Elemente zu einem gewissen Grad zu trennen.
Wenn beispielsweise der einzige Platzhalter, den eine bestimmte Zeichenkette unterstützt, eine "willkürliche Zeichenkette aus einer oder mehreren Ziffern" darstellt, könnte man die Zeichenkette durch Konvertieren aller Folgen aufeinanderfolgender Ziffern und / oder Platzhalterzeichen in eine einzelnes "String-Zeichen" Platzhalterzeichen. Wenn # für eine Ziffer steht, dann werden die Zeichenfolgen abc123, abc #, abc456 und abc # 93 # 22 # 7 alle auf den gleichen Wert wie abc # gehashed, aber abc # b, abc123b usw. könnten auf einen anderen Hashwert gesetzt werden Wert. Abhängig von der Verteilung der Zeichenfolgen können solche Unterscheidungen eine bessere Leistung erbringen oder auch nicht, als einen konstanten Wert zurückzugeben.
Beachten Sie, dass selbst wenn man GetHashCode
so implementiert, dass gleiche Objekte gleiche Hashes ergeben, einige Auflistungen immer noch merkwürdig funktionieren, wenn die Gleichheits-Methode keine Äquivalenz-Beziehung implementiert. Wenn beispielsweise eine Sammlung foo
Elemente mit den Schlüsseln "abc1" und "abc2" enthält, können Versuche, auf foo["abc#"]
zuzugreifen, das erste Element oder das zweite Element willkürlich zurückgeben. Versuche, den Schlüssel "abc #" zu löschen, können willkürlich eines oder beide Elemente entfernen oder können nach dem Löschen eines Elements fehlschlagen (die erwartete Nachbedingung wäre nicht erfüllt, da abc#
auch nach dem Löschen in der Sammlung wäre).
Anstatt zu versuchen, die Gleichheit des Hash-Codes mit jinx Equals
zu vergleichen, besteht ein alternativer Ansatz darin, ein Wörterbuch zu haben, das für jede mögliche Platzhalterzeichenfolge mindestens eine Hauptsammlungszeichenfolge enthält möglicherweise übereinstimmen. Wenn also viele Zeichenfolgen mit abc # übereinstimmen, könnten sie alle unterschiedliche Hash-Codes haben; Wenn ein Benutzer "abc #" als Suchanforderung eingibt, sucht das System im Wildcard-Wörterbuch nach "abc #" und erhält eine Liste aller Zeichenketten, die mit diesem Muster übereinstimmen, die dann einzeln im Hauptwörterbuch nachgeschlagen werden können .
Tags und Links c# wildcard hashcode iequatable