Wie funktioniert die Standardimplementierung für GetHashCode()
? Und handhabt es Strukturen, Klassen, Arrays usw. effizient und gut genug?
Ich versuche zu entscheiden, in welchen Fällen ich meine eigenen packen soll und in welchen Fällen ich mich sicher darauf verlassen kann, dass die Standardimplementierung gut funktioniert. Ich möchte das Rad nicht neu erfinden, wenn es überhaupt möglich ist.
.net
hash
gethashcode
Fung
quelle
quelle
GetHashCode()
schon außer Kraft gesetzt wird) unter VerwendungSystem.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj)
Antworten:
InternalGetHashCode wird einer ObjectNative :: GetHashCode- Funktion in der CLR zugeordnet, die folgendermaßen aussieht:
Die vollständige Implementierung von GetHashCodeEx ist ziemlich umfangreich, sodass es einfacher ist, nur eine Verknüpfung zum C ++ - Quellcode herzustellen .
quelle
string
überschreibtGetHashCode
. Angenommen, Sie möchten zählen, wie oft verschiedene SteuerelementePaint
Ereignisse verarbeiten. Sie könnten ein verwendenDictionary<Object, int[]>
(jedesint[]
gespeicherte würde genau ein Element enthalten).Für eine Klasse sind die Standardeinstellungen im Wesentlichen Referenzgleichheit, und das ist normalerweise in Ordnung. Wenn Sie eine Struktur schreiben, ist es üblicher, die Gleichheit zu überschreiben (nicht zuletzt, um das Boxen zu vermeiden), aber es ist sehr selten, dass Sie trotzdem eine Struktur schreiben!
Wenn Sie die Gleichheit überschreiben, sollten Sie immer eine Übereinstimmung haben
Equals()
undGetHashCode()
(dh für zwei Werte, wennEquals()
true zurückgegeben wird, müssen sie denselben Hash-Code zurückgeben, aber die Umkehrung ist nicht erforderlich) - und es ist üblich, auch==
/!=
operator bereitzustellen , und häufig zu auch implementierenIEquatable<T>
.Zum Generieren des Hash-Codes wird häufig eine faktorisierte Summe verwendet, da dadurch Kollisionen mit gepaarten Werten vermieden werden - beispielsweise für einen einfachen 2-Feld-Hash:
Dies hat den Vorteil, dass:
etc - was häufig vorkommt, wenn nur eine ungewichtete Summe oder xor (
^
) usw. verwendet wird.quelle
unchecked
. Glücklicherweiseunchecked
ist dies die Standardeinstellung in C #, aber es ist besser, sie explizit zu machen. bearbeitetIn der Dokumentation zur
GetHashCode
Methode für Object heißt es: "Die Standardimplementierung dieser Methode darf nicht als eindeutige Objektkennung für Hashing-Zwecke verwendet werden." und der für ValueType lautet: "Wenn Sie die GetHashCode-Methode des abgeleiteten Typs aufrufen, ist der Rückgabewert wahrscheinlich nicht für die Verwendung als Schlüssel in einer Hash-Tabelle geeignet." .Die grundlegenden Datentypen wie
byte
,short
,int
,long
,char
undstring
ein gutes GetHashCode - Methode implementieren. Einige andere Klassen und Strukturen, wiePoint
zum Beispiel, implementieren eineGetHashCode
Methode, die für Ihre spezifischen Anforderungen geeignet sein kann oder nicht. Sie müssen es nur ausprobieren, um zu sehen, ob es gut genug ist.Die Dokumentation für jede Klasse oder Struktur kann Ihnen sagen, ob sie die Standardimplementierung überschreibt oder nicht. Wenn es nicht überschrieben wird, sollten Sie Ihre eigene Implementierung verwenden. Für alle Klassen oder Strukturen, die Sie selbst erstellen und in denen Sie die
GetHashCode
Methode verwenden müssen, sollten Sie eine eigene Implementierung erstellen , die die entsprechenden Elemente zur Berechnung des Hash-Codes verwendet.quelle
Da ich keine Antwort finden konnte, die erklärt, warum wir überschreiben sollten
GetHashCode
undEquals
für benutzerdefinierte Strukturen und warum die Standardimplementierung "wahrscheinlich nicht als Schlüssel in einer Hash-Tabelle geeignet ist", werde ich einen Link zu diesem Blog hinterlassen Beitrag , der erklärt, warum mit einem realen Beispiel eines aufgetretenen Problems.Ich empfehle, den gesamten Beitrag zu lesen, aber hier ist eine Zusammenfassung (Hervorhebung und Klarstellung hinzugefügt).
Grund, warum der Standard-Hash für Strukturen langsam und nicht sehr gut ist:
In der Post beschriebenes Problem der realen Welt:
Um die Frage zu beantworten, "in welchen Fällen ich meine eigene packen sollte und in welchen Fällen ich mich sicher auf die Standardimplementierung verlassen kann", sollten Sie zumindest bei Strukturen überschreiben
Equals
undGetHashCode
wann immer Ihre benutzerdefinierte Struktur als verwendet werden könnte Geben Sie eine Hash-Tabelle ein oderDictionary
.Ich würde auch empfehlen,
IEquatable<T>
in diesem Fall zu implementieren , um Boxen zu vermeiden.Wie die anderen Antworten sagten, ist beim Schreiben einer Klasse der Standard-Hash mit Referenzgleichheit normalerweise in Ordnung, daher würde ich mich in diesem Fall nicht darum kümmern, es sei denn, Sie müssen überschreiben
Equals
(dann müssten SieGetHashCode
entsprechend überschreiben ).quelle
Wenn Sie Equals überschreiben, möchten Sie im Allgemeinen GetHashCode überschreiben. Der Grund dafür ist, dass beide verwendet werden, um die Gleichheit Ihrer Klasse / Struktur zu vergleichen.
Gleich wird verwendet, wenn Foo A, B überprüft wird;
if (A == B)
Da wir wissen, dass der Zeiger wahrscheinlich nicht übereinstimmt, können wir die internen Mitglieder vergleichen.
GetHashCode wird im Allgemeinen von Hash-Tabellen verwendet. Der von Ihrer Klasse generierte Hashcode sollte für einen Klassen-Status immer der gleiche sein.
Ich mache normalerweise,
Einige werden sagen, dass der Hashcode nur einmal pro Objektlebensdauer berechnet werden sollte, aber ich stimme dem nicht zu (und ich liege wahrscheinlich falsch).
Wenn Sie die von object bereitgestellte Standardimplementierung verwenden, sind diese nicht gleich, es sei denn, Sie haben denselben Verweis auf eine Ihrer Klassen. Durch Überschreiben von Equals und GetHashCode können Sie Gleichheit basierend auf internen Werten und nicht auf der Objektreferenz melden.
quelle
Wenn Sie nur mit POCOs zu tun haben, können Sie dieses Dienstprogramm verwenden, um Ihr Leben etwas zu vereinfachen:
...
quelle