In .NET wird die GetHashCode
Methode an vielen Stellen in den .NET-Basisklassenbibliotheken verwendet. Die ordnungsgemäße Implementierung ist besonders wichtig, um Elemente in einer Sammlung schnell zu finden oder um die Gleichheit zu bestimmen.
Gibt es einen Standardalgorithmus oder eine bewährte Methode für die Implementierung GetHashCode
für meine benutzerdefinierten Klassen, damit die Leistung nicht beeinträchtigt wird?
.net
algorithm
hashcode
gethashcode
Bitbonk
quelle
quelle
GetHashCode
. Ich hoffe es wäre hilfreich für andere. Richtlinien und Regeln für GetHashCode geschrieben von Eric LippertGetHashCode()
noch in sehr vielen Implementierungen von verwendetEquals()
. Das habe ich mit dieser Aussage gemeint.GetHashCode()
insideEquals()
wird häufig als Verknüpfung zur Bestimmung der Ungleichung verwendet , da zwei Objekte, die einen unterschiedlichen Hash-Code haben, Objekte sein müssen, die nicht gleich sind, und der Rest der Gleichheitsprüfung nicht ausgeführt werden muss.GetHashCode()
undEquals()
müssen alle Felder beider Objekte betrachten (Equals muss dies tun, wenn die Hashcodes gleich oder nicht aktiviert sind). Aus diesem Grund ist ein Anruf nachGetHashCode()
innenEquals()
häufig redundant und kann die Leistung beeinträchtigen.Equals()
Möglicherweise kann es auch zu einem Kurzschluss kommen, wodurch es viel schneller wird. In einigen Fällen werden die Hashcodes jedoch zwischengespeichert, wodurch dieGetHashCode()
Überprüfung schneller und lohnender wird. Weitere Informationen finden Sie in dieser Frage .Antworten:
Normalerweise gehe ich mit so etwas wie der Implementierung in Josh Blochs fabelhaftem Effective Java um . Es ist schnell und erzeugt einen ziemlich guten Hash, der wahrscheinlich keine Kollisionen verursacht. Wählen Sie zwei verschiedene Primzahlen, z. B. 17 und 23, und tun Sie Folgendes:
Wie in den Kommentaren erwähnt, ist es möglicherweise besser, stattdessen eine große Primzahl auszuwählen, mit der multipliziert werden soll. Anscheinend ist 486187739 gut ... und obwohl die meisten Beispiele, die ich mit kleinen Zahlen gesehen habe, Primzahlen verwenden, gibt es zumindest ähnliche Algorithmen, bei denen häufig Nicht-Primzahlen verwendet werden. In dem nicht ganz FNV- Beispiel später habe ich zum Beispiel Zahlen verwendet, die anscheinend gut funktionieren - aber der Anfangswert ist keine Primzahl. (Die Multiplikationskonstante ist jedoch eine Primzahl. Ich weiß nicht genau, wie wichtig das ist.)
Dies ist
XOR
aus zwei Hauptgründen besser als die übliche Praxis, Hashcodes zu verwenden. Angenommen, wir haben einen Typ mit zweiint
Feldern:Der frühere Algorithmus wird übrigens derzeit vom C # -Compiler für anonyme Typen verwendet.
Diese Seite bietet einige Optionen. Ich denke, für die meisten Fälle ist das oben Genannte "gut genug" und es ist unglaublich einfach, sich zu erinnern und richtig zu machen. Die FNV- Alternative ist ähnlich einfach, verwendet jedoch unterschiedliche Konstanten und
XOR
nichtADD
als Kombinationsoperation. Es sieht ungefähr so aus wie der folgende Code, aber der normale FNV-Algorithmus arbeitet mit einzelnen Bytes, sodass eine Änderung erforderlich wäre, um eine Iteration pro Byte anstelle eines 32-Bit-Hashwerts durchzuführen. FNV ist auch für variable Datenlängen ausgelegt, während wir es hier immer für die gleiche Anzahl von Feldwerten verwenden. Kommentare zu dieser Antwort deuten darauf hin, dass der Code hier nicht so gut funktioniert (im getesteten Beispielfall) wie der oben beschriebene Additionsansatz.Beachten Sie, dass Sie im Idealfall verhindern sollten, dass sich Ihr gleichheitsempfindlicher (und damit hashcodeempfindlicher) Status ändert, nachdem Sie ihn einer Sammlung hinzugefügt haben, die vom Hashcode abhängt.
Gemäß Dokumentation :
quelle
Dictionary<TKey,TValue>
gute Verteilung modulo bestimmter Primzahlen vorausgesetzt wird. Und 23 ist einer von ihnen. Wenn Sie also ein Wörterbuch mit Kapazität 23 haben,GetHashCode
beeinflusst nur der letzte Beitrag den zusammengesetzten Hashcode. Also würde ich lieber 29 statt 23 verwenden.null
- was nicht gleichbedeutend ist mit dem Ignorieren des Feldes.Anonymer Typ
Microsoft bietet bereits einen guten generischen HashCode-Generator: Kopieren Sie einfach Ihre Eigenschafts- / Feldwerte in einen anonymen Typ und hashen Sie ihn:
Dies funktioniert für eine beliebige Anzahl von Eigenschaften. Es wird kein Boxen verwendet. Es wird nur der Algorithmus verwendet, der bereits im Framework für anonyme Typen implementiert ist.
ValueTuple - Update für C # 7
Wie @cactuaroid in den Kommentaren erwähnt, kann ein Wertetupel verwendet werden. Dies spart ein paar Tastenanschläge und wird vor allem nur auf dem Stapel ausgeführt (kein Müll):
(Hinweis: Die ursprüngliche Technik, bei der anonyme Typen verwendet werden, scheint ein Objekt auf dem Heap zu erstellen, dh Garbage, da anonyme Typen als Klassen implementiert werden, obwohl dies möglicherweise vom Compiler optimiert wird. Es wäre interessant, diese Optionen zu vergleichen, aber die Die Tupeloption sollte überlegen sein.)
quelle
GetHashCode
Implementierung ist sehr effektiv (übrigens die gleiche wie in der Antwort von Jon Skeet), aber das einzige Problem bei dieser Lösung ist, dass Sie bei jedemGetHashCode
Aufruf eine neue Instanz generieren .new { PropA, PropB, PropC, PropD }.GetHashCode()
zuNew With {Key PropA}.GetHashCode()
Andernfalls gibt GetHashCode nicht denselben Hashcode für verschiedene Objekte mit denselben 'identifizierenden' Eigenschaften zurück.Hier ist mein Hashcode-Helfer.
Der Vorteil ist, dass generische Typargumente verwendet werden und daher kein Boxen verursacht wird:
Außerdem verfügt es über eine Erweiterungsmethode, um eine flüssige Schnittstelle bereitzustellen, sodass Sie sie folgendermaßen verwenden können:
oder so:
quelle
T[]
separat, wie es bereits istIEnumerable<T>
Ich habe eine Hashing-Klasse in der Helper-Bibliothek, die ich für diesen Zweck verwende.
Dann können Sie es einfach verwenden als:
Ich habe die Leistung nicht bewertet, daher ist jedes Feedback willkommen.
quelle
unchecked
ist, Ausnahmen beim Überlauf zu vermeiden, die bei gewünscht werdenGetHashCode
. Es ist also nicht falsch, wenn der Wert überläuftint
und es überhaupt nicht weh tut.null
es vollständig übersprungen wird, zu unerwarteten Ergebnissen führen. Anstatt sie zu überspringen, sollten Sie nur einen konstanten Wert verwenden, anstattinput[i].GetHashCode()
wanninput[i]
null ist.Hier ist meine Hilfsklasse mit Jon Skeets Implementierung .
Verwendungszweck:
Wenn Sie vermeiden möchten, eine Erweiterungsmethode für System.Int32 zu schreiben:
Es vermeidet weiterhin jede Heap-Zuordnung und wird genauso verwendet:
Bearbeiten (Mai 2018):
EqualityComparer<T>.Default
Getter ist jetzt ein JIT-Intrinsic - die Pull-Anfrage wird von Stephen Toub in diesem Blog-Beitrag erwähnt .quelle
var h = Equals(obj, default(T)) ? 0 : obj.GetHashCode();
obj != null
zu einerbox
Anweisung kompiliert wird, die Speicher zuweist, wennT
es sich um einen Werttyp handelt. Stattdessen können Sie verwenden,obj.Equals(null)
was zu einem virtuellen Aufruf derEquals
Methode kompiliert wird.this.hashCode != h
. Es würde nicht den gleichen Wert zurückgeben..NET Standard 2.1 und höher
Wenn Sie .NET Standard 2.1 oder höher verwenden, können Sie die System.HashCode- Struktur verwenden. Es gibt zwei Methoden, um es zu verwenden:
HashCode.Combine
Die
Combine
Methode kann verwendet werden, um einen Hash-Code mit bis zu acht Objekten zu erstellen.HashCode.Add
Die
Add
Methode hilft Ihnen beim Umgang mit Sammlungen:GetHashCode leicht gemacht
Sie können den vollständigen Blog-Beitrag ' GetHashCode Made Easy ' für weitere Details und Kommentare lesen .
Anwendungsbeispiel
Implementierung
Was macht einen guten Algorithmus aus?
Geschwindigkeit
Der Algorithmus, der einen Hash-Code berechnet, muss schnell sein. Ein einfacher Algorithmus wird normalerweise schneller sein.
Deterministisch
Der Hashing-Algorithmus muss deterministisch sein, dh bei gleicher Eingabe muss er immer dieselbe Ausgabe erzeugen.
Kollisionen reduzieren
Der Algorithmus, der einen Hash-Code berechnet, muss die Hash-Kollisionen auf ein Minimum beschränken. Eine Hash-Kollision ist eine Situation, die auftritt, wenn zwei Aufrufe
GetHashCode
von zwei verschiedenen Objekten identische Hash-Codes erzeugen. Beachten Sie, dass Kollisionen zulässig sind (einige haben die falschen Vorstellungen, dass dies nicht der Fall ist), sie sollten jedoch auf ein Minimum beschränkt werden.Eine gute Hash-Funktion sollte die erwarteten Eingaben über ihren Ausgabebereich so gleichmäßig wie möglich abbilden. Es sollte einheitlich sein.
Verhindern Sie DoS
In .NET Core erhalten Sie bei jedem Neustart einer Anwendung unterschiedliche Hash-Codes. Dies ist eine Sicherheitsfunktion, um Denial-of-Service-Angriffe (DoS) zu verhindern. Für .NET Framework sollten Sie diese Funktion aktivieren, indem Sie die folgende App.config-Datei hinzufügen:
Aufgrund dieser Funktion sollten Hash-Codes niemals außerhalb der Anwendungsdomäne verwendet werden, in der sie erstellt wurden. Sie sollten niemals als Schlüsselfelder in einer Sammlung verwendet werden und niemals beibehalten werden.
Lesen Sie mehr dazu hier .
Kryptographisch sicher?
Der Algorithmus muss keine kryptografische Hash-Funktion sein . Das heißt, es muss nicht die folgenden Bedingungen erfüllen:
quelle
In den meisten Fällen, in denen Equals () mehrere Felder vergleicht, spielt es keine Rolle, ob GetHash () auf einem oder mehreren Feldern hascht. Sie müssen nur sicherstellen, dass die Berechnung des Hashs wirklich billig ( bitte keine Zuordnungen ) und schnell ( keine umfangreichen Berechnungen und sicherlich keine Datenbankverbindungen) ist und eine gute Verteilung bietet.
Das schwere Heben sollte Teil der Equals () -Methode sein. Der Hash sollte eine sehr billige Operation sein, um das Aufrufen von Equals () für so wenige Elemente wie möglich zu ermöglichen.
Und noch ein letzter Tipp: Verlassen Sie sich nicht darauf, dass GetHashCode () über mehrere Anwendungsläufe hinweg stabil ist . Viele .Net-Typen garantieren nicht, dass ihre Hash-Codes nach einem Neustart gleich bleiben. Daher sollten Sie den Wert von GetHashCode () nur für Speicherdatenstrukturen verwenden.
quelle
GetHashCode
, Speicherzuweisungen durchzuführen, vorausgesetzt, dies geschieht nur bei der ersten Verwendung (bei nachfolgenden Aufrufen wird einfach ein zwischengespeichertes Ergebnis zurückgegeben). Wichtig ist nicht, dass man große Anstrengungen unternimmt, um Kollisionen zu vermeiden, sondern dass man "systemische" Kollisionen vermeidet. Wenn ein Typ zweiint
Felder hatoldX
und sichnewX
häufig um eins unterscheiden, würde ein Hashwert vonoldX^newX
90% solcher Datensätze Hashwerte von 1, 2, 4 oder 8 zuweisen. Die Verwendung vonoldX+newX
[nichtBis vor kurzem wäre meine Antwort Jon Skeets hier sehr nahe gekommen. Ich habe jedoch kürzlich ein Projekt gestartet, das Zweierpotenz-Hash-Tabellen verwendet, dh Hash-Tabellen, bei denen die Größe der internen Tabelle 8, 16, 32 usw. beträgt. Es gibt einen guten Grund, Primzahlengrößen zu bevorzugen, aber dort sind auch einige Vorteile für Zweierpotenzgrößen.
Und es saugte ziemlich viel. Nach einigem Experimentieren und Nachforschen begann ich, meine Hashes mit den folgenden Schritten neu zu hashen:
Und dann saugte meine Zwei-Potenz-Hash-Tabelle nicht mehr.
Das hat mich allerdings gestört, weil das oben genannte nicht funktionieren sollte. Genauer gesagt sollte es nicht funktionieren, es sei denn, das Original
GetHashCode()
war auf ganz besondere Weise schlecht.Das erneute Mischen eines Hashcodes kann einen großartigen Hashcode nicht verbessern, da der einzig mögliche Effekt darin besteht, dass wir einige weitere Kollisionen einführen.
Das erneute Mischen eines Hash-Codes kann einen schrecklichen Hash-Code nicht verbessern, da der einzig mögliche Effekt darin besteht, dass wir beispielsweise eine große Anzahl von Kollisionen auf Wert 53 in eine große Anzahl von Wert 18.3487.291 ändern.
Das erneute Mischen eines Hash-Codes kann nur einen Hash-Code verbessern, der zumindest ziemlich gut absolute Kollisionen in seinem gesamten Bereich (2 32 mögliche Werte) vermeidet, aber Kollisionen schlecht vermeidet, wenn er für die tatsächliche Verwendung in einer Hash-Tabelle heruntergefahren wird. Das einfachere Modulo einer Zweierpotenztabelle machte dies deutlicher, wirkte sich jedoch auch negativ auf die häufigeren Primzahltabellen aus, was jedoch nicht so offensichtlich war (die zusätzliche Arbeit beim Aufwärmen würde den Vorteil überwiegen , aber der Nutzen wäre immer noch da).
Bearbeiten: Ich habe auch Open-Addressing verwendet, was auch die Kollisionsempfindlichkeit erhöht hätte, vielleicht mehr als die Tatsache, dass es sich um eine Zweierpotenz handelt.
Und nun, es war beunruhigend, um wie viel die
string.GetHashCode()
Implementierungen in .NET (oder hier zu studieren ) auf diese Weise verbessert werden konnten (in der Größenordnung von Tests, die aufgrund weniger Kollisionen etwa 20 bis 30 Mal schneller ablaufen) und beunruhigender, wie viel meine eigenen Hash-Codes könnte verbessert werden (viel mehr als das).Alle GetHashCode () -Implementierungen, die ich in der Vergangenheit codiert und tatsächlich als Grundlage für die Antworten auf dieser Site verwendet hatte, waren viel schlechter als ich . Die meiste Zeit war es "gut genug" für viele Zwecke, aber ich wollte etwas Besseres.
Also legte ich dieses Projekt beiseite (es war sowieso ein Lieblingsprojekt) und begann zu überlegen, wie man schnell einen guten, gut verteilten Hash-Code in .NET erstellt.
Am Ende habe ich mich entschlossen , SpookyHash nach .NET zu portieren . In der Tat ist der obige Code eine Fast-Path-Version der Verwendung von SpookyHash, um eine 32-Bit-Ausgabe von einer 32-Bit-Eingabe zu erzeugen.
Jetzt ist SpookyHash kein guter, schnell zu merkender Code. Mein Port davon ist noch weniger, weil ich viel von Hand für eine bessere Geschwindigkeit * eingefügt habe. Aber dafür ist die Wiederverwendung von Code gedacht.
Dann habe ich dieses Projekt beiseite gelegt, weil genau wie das ursprüngliche Projekt die Frage aufgeworfen hatte, wie ein besserer Hash-Code erzeugt werden kann, so hat dieses Projekt die Frage aufgeworfen, wie ein besserer .NET-Speicher erstellt werden kann.
Dann kam ich zurück und erzeugte viele Überladungen, um fast alle nativen Typen (außer
decimal
†) einfach in einen Hash-Code einzugeben.Es ist schnell, wofür Bob Jenkins den größten Teil der Anerkennung verdient, da sein ursprünglicher Code, von dem ich portiert habe, noch schneller ist, insbesondere auf 64-Bit-Computern, für die der Algorithmus optimiert ist.
Der vollständige Code kann unter https://bitbucket.org/JonHanna/spookilysharp/src eingesehen werden. Beachten Sie jedoch, dass der obige Code eine vereinfachte Version davon ist.
Da es jetzt bereits geschrieben ist, kann man es leichter verwenden:
Es werden auch Startwerte verwendet. Wenn Sie also mit nicht vertrauenswürdigen Eingaben umgehen müssen und sich vor Hash-DoS-Angriffen schützen möchten, können Sie einen Startwert basierend auf der Verfügbarkeit oder ähnlichem festlegen und die Ergebnisse für Angreifer unvorhersehbar machen:
* Eine große Überraschung dabei ist das Hand-Inlining einer Rotationsmethode, die
(x << n) | (x >> -n)
verbesserte Ergebnisse liefert . Ich wäre mir sicher gewesen, dass der Jitter das für mich eingefügt hätte, aber die Profilerstellung zeigte etwas anderes.†
decimal
ist aus .NET-Sicht nicht nativ, obwohl es aus C # stammt. Das Problem dabei ist, dass seine eigeneGetHashCode()
Präzision als signifikant behandelt wird, während seine eigeneEquals()
dies nicht tut. Beide sind gültige Entscheidungen, aber nicht so gemischt. Bei der Implementierung Ihrer eigenen Version müssen Sie sich für die eine oder andere Version entscheiden, aber ich kann nicht wissen, welche Sie möchten.‡ Zum Vergleich. Bei Verwendung für eine Zeichenfolge ist der SpookyHash auf 64 Bit erheblich schneller als
string.GetHashCode()
auf 32 Bit, was etwas schneller ist alsstring.GetHashCode()
auf 64 Bit, was erheblich schneller ist als der SpookyHash auf 32 Bit, obwohl er immer noch schnell genug ist, um eine vernünftige Wahl zu sein.quelle
long
Werte für die Zwischenergebnisse zu verwenden und dann das Endergebnis auf einen zu reduzierenint
. Scheint das eine gute Idee zu sein? Ich mache mir Sorgen, dass man zB hash = (hash * 31) + nextField verwendet, dann wirken sich Paare von übereinstimmenden Werten nur auf die oberen 27 Bits des Hash aus. Wenn Sie die Berechnung auf a ausdehnenlong
und Inhalte einwickeln, wird diese Gefahr minimiert..Update()
mit den mehreren Werten gemäß der obigen Antwort reicht aus.Das ist ein guter:
Und so geht's:
quelle
GetHashCode()
Methode haben, sodass Sie die Methode immer mit demparams
Array-Parameter verwenden können. Oder fehlt mir hier etwas?h += (h << 10); h ^= (h >> 6); h += (h << 3); h ^= (h >> 11); h += (h << 15);
nachfolgenden Shift / Xor-Schritte ( haben einen Code-Geruch: Sie hängen von keiner der Eingaben ab und sehen für mich schrecklich überflüssig aus.Ab https://github.com/dotnet/coreclr/pull/14863 gibt es eine neue Möglichkeit, Hash-Codes zu generieren, die sehr einfach ist! Einfach schreiben
Dadurch wird ein Qualitäts-Hash-Code generiert, ohne dass Sie sich um die Implementierungsdetails kümmern müssen.
quelle
HashCode
Änderungen für corefx wurden nur wenige Stunden vor Ihrem Kommentar zusammengeführt :) Der Typ soll in .NET Core 2.1 ausgeliefert werden.Hier ist eine weitere fließende Implementierung des oben von Jon Skeet veröffentlichten Algorithmus , die jedoch keine Zuweisungen oder Boxoperationen enthält:
Verwendungszweck:
Der Compiler stellt sicher, dass
HashValue
aufgrund der generischen Typbeschränkung nicht mit einer Klasse aufgerufen wird. Es gibt jedoch keine Compiler-Unterstützung,HashObject
da durch Hinzufügen eines generischen Arguments auch eine Boxoperation hinzugefügt wird.quelle
Hier ist mein simpler Ansatz. Ich verwende dafür das klassische Builder-Muster. Es ist typsicher (kein Boxen / Unboxing) und auch kompatibel mit .NET 2.0 (keine Erweiterungsmethoden usw.).
Es wird so verwendet:
Und hier ist die akute Builder-Klasse:
quelle
AddItems<T>(params T[] items)
Methode in der Hilfsklasse häufiger verwenden (alsAddItem(T)
jedes Mal aufzurufen ).this.result * Prime2 * item.GetHashCode()
bei häufigem Gebrauchthis.result * Prime2 + item.GetHashCode()
?AddItems<T>(params T[] items)
öfter verwenden, weiltypeof(T1) != typeof(T2)
usw.ReSharper- Benutzer können GetHashCode, Equals und andere mit generieren
ReSharper -> Edit -> Generate Code -> Equality Members
.quelle
Wenn wir (hoffentlich) nicht mehr als 8 Eigenschaften haben, ist hier eine andere Alternative.
ValueTuple
ist eine Struktur und scheint eine solideGetHashCode
Implementierung zu haben .Das heißt, wir könnten dies einfach tun:
Werfen wir einen Blick auf .NET Core aktuelle Implementierung für
ValueTuple
‚sGetHashCode
.Dies ist aus
ValueTuple
:Und das ist von
HashHelper
:Auf Englisch:
Es wäre schön, mehr über die Eigenschaften dieses ROL-5-Hashcode-Algorithmus zu erfahren.
Bedauerlicherweise Aufschieben auf
ValueTuple
für unsere eigenenGetHashCode
kann nicht so schnell, wie wir möchten und erwarten. Dieser Kommentar in einer verwandten Diskussion zeigt, dass das direkte AnrufenHashHelpers.Combine
performanter ist. Auf der anderen Seite ist dieser intern, also müssten wir den Code kopieren und viel von dem opfern, was wir hier gewonnen haben. Wir wären auch dafür verantwortlich, uns daran zu erinnern, zuerstCombine
den zufälligen Samen zu verwenden. Ich weiß nicht, was die Konsequenzen sind, wenn wir diesen Schritt überspringen.quelle
h1 >> 27
ist 0, um es zu ignorieren, isth1 << 5
gleich,h1 * 32
also ist es dasselbe wieh1 * 33 ^ h2
. Nach dieser Seite heißt es "Modified Bernstein".Der Großteil meiner Arbeit wird mit Datenbankkonnektivität erledigt, was bedeutet, dass meine Klassen alle eine eindeutige Kennung aus der Datenbank haben. Ich verwende immer die ID aus der Datenbank, um den Hashcode zu generieren.
quelle
_id.GetHashCode
da die Absicht klar ist.Ziemlich ähnlich wie bei Nightcoder, nur dass es einfacher ist, Primzahlen zu erhöhen, wenn Sie möchten.
PS: Dies ist eine dieser Situationen, in denen Sie ein wenig in den Mund kotzen und wissen, dass dies mit 9 Standardeinstellungen in eine Methode umgewandelt werden könnte, die jedoch langsamer ist. Schließen Sie also einfach Ihre Augen und versuchen Sie, sie zu vergessen.
quelle
Ich bin auf ein Problem mit Gleitkommazahlen und Dezimalstellen gestoßen, bei dem die oben als Antwort ausgewählte Implementierung verwendet wurde.
Dieser Test schlägt fehl (Floats; Hash ist der gleiche, obwohl ich 2 Werte auf negativ geschaltet habe):
Aber dieser Test besteht (mit Ints):
Ich habe meine Implementierung dahingehend geändert, dass GetHashCode nicht für die primitiven Typen verwendet wird, und es scheint besser zu funktionieren
quelle
unchecked
keinen Einfluss hatConvert.ToInt32
:uint
,long
,float
,double
unddecimal
kann alle Überlauf hier.Microsoft führt für verschiedene Arten von Hashing ...
Ich kann mir vorstellen, dass Sie für mehrere große Int Folgendes verwenden können:
Und das Gleiche gilt für Multi-Type: Alle werden zuerst in
int
using konvertiert,GetHashCode()
dann werden die int-Werte xor'ed und das Ergebnis ist Ihr Hash.Für diejenigen, die Hash als ID verwenden (ich meine einen eindeutigen Wert), ist Hash natürlich auf eine Anzahl von Ziffern beschränkt. Ich denke, es waren 5 Bytes für den Hashing-Algorithmus, mindestens MD5.
Sie können mehrere Werte in einen Hash-Wert umwandeln, von denen einige identisch sind. Verwenden Sie ihn daher nicht als Bezeichner. (Vielleicht werde ich eines Tages Ihre Komponente verwenden)
quelle
Dies ist eine statische Hilfsklasse, die die Implementierung von Josh Bloch implementiert. und bietet explizite Überladungen, um das Boxen zu "verhindern" und den Hash speziell für die langen Grundelemente zu implementieren.
Sie können einen Zeichenfolgenvergleich übergeben, der Ihrer gleichwertigen Implementierung entspricht.
Da die Hash-Ausgabe immer ein int ist, können Sie einfach Hash-Aufrufe verketten.
quelle
HashKeysAndValues
Methode wurde behoben: Sie wird aufgerufenHashKeyAndValue
.Für den Fall, dass Sie
HashCode
aus füllen möchtennetstandard2.1
Hinweis: Bei Verwendung mit
struct
wird aufgrund des Boxens Speicher zugewiesenquelle