Implementieren Sie GetHashCode für eine Klasse mit Platzhaltergleichheit

7

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?

    
Brondahl 22.07.2014, 15:37
quelle

9 Antworten

9

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.

    
Rich 22.07.2014, 15:49
quelle
6

In diesem Fall würde ich anstelle der Operatoren eine neue oder Erweiterungsmethode, WildcardEquals(other) , erstellen.

Ich würde nicht empfehlen, diese Art von Komplexität zu verstecken.

    
Dave Bish 22.07.2014 15:43
quelle
4

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.

    
AlexD 22.07.2014 15:51
quelle
3

Die einzige mögliche Idee, die ich sehe, ist, zumindest die Länge auszunutzen, z. B .:

%Vor%     
BartoszKP 22.07.2014 15:41
quelle
2

Es ist empfohlen , aber nicht zwingend erforderlich. Wenn Sie diese benutzerdefinierte Implementierung von GetHashCode nicht benötigen, tun Sie es einfach nicht.

    
Tigran 22.07.2014 15:41
quelle
2

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.

    
yjo 22.07.2014 15:48
quelle
2

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.

    
Tim Perry 22.07.2014 15:49
quelle
1

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.

    
Tim 22.07.2014 15:43
quelle
1

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 .

    
supercat 22.07.2014 20:41
quelle

Tags und Links