Wenn Sie das Refactoring-Menü von Visual Studio verwenden, um einer Klasse wie dieser eine GetHashCode-Implementierung hinzuzufügen:
und wählen Sie die einzige int-Eigenschaft in der Klasse aus:
Dieser Code wird in .NET Framework generiert:
public override int GetHashCode()
{
return -1937169414 + Value.GetHashCode();
}
(Es wird HashCode.Combine(Value)
stattdessen auf .NET Core generiert. Ich bin mir nicht sicher, ob es denselben Wert enthält.)
Was ist das Besondere an diesem Wert? Warum wird Visual Studio nicht Value.GetHashCode()
direkt verwendet? Soweit ich weiß, hat dies keinen wirklichen Einfluss auf die Hash-Verteilung. Da es sich nur um eine Addition handelt, würden sich aufeinanderfolgende Werte immer noch zusammen ansammeln.
BEARBEITEN: Ich habe dies nur mit verschiedenen Klassen mit Value
Eigenschaften versucht, aber anscheinend wirkt sich der Eigenschaftsname auf die generierte Anzahl aus. Wenn Sie beispielsweise die Eigenschaft in umbenennen Halue
, lautet die Nummer 387336856. Vielen Dank an Gökhan Kurt, der darauf hingewiesen hat.
quelle
int
.-1937169414
ist eine ganzzahlige Multiplikation von-1521134295
und-783812246
. Die bedeutendere Zahl ist hier,-1521134295
die in jeder Hashcode-Berechnung erscheint.-783812246
ist die Startnummer. Eine Startnummer wird basierend auf der Anzahl der Elemente in der Gleichung ausgewählt. In anonymen Klassen wird die Startnummer basierend auf Feldnamen berechnet. Es gibt also so viele Startzahlen wie ganze Zahlen. Wir können davon ausgehen, dass eine Startnummer zufällig ist. Was die Bedeutung von-1521134295
betrifft, denke ich, dass es die Kollision reduziert und nur ein Insider-Entwickler in der Lage wäre, genau zu beantworten, wie.Antworten:
Wenn Sie
-1521134295
in den Microsoft-Repositorys suchen, werden Sie feststellen, dass diese häufig angezeigt werdenDie meisten Suchergebnisse befinden sich in den
GetHashCode
Funktionen, aber alle haben die folgende FormDie erste
hashCode * -1521134295 = SOME_CONSTANT * -1521134295
wird während der Generierungszeit vom Generator oder während der Kompilierungszeit von CSC vormultipliziert. Das ist der Grund dafür-1937169414
in Ihrem CodeWenn Sie sich eingehender mit den Ergebnissen befassen , wird der Teil zur Codegenerierung angezeigt , der in der Funktion CreateGetHashCodeMethodStatements enthalten ist
Wie Sie sehen können, hängt der Hash von den Symbolnamen ab. In dieser Funktion wird die Konstante auch aufgerufen
permuteValue
, wahrscheinlich weil die Bits nach der Multiplikation irgendwie umlaufenEs gibt einige Muster, wenn wir den Wert binär anzeigen:
101001 010101010101010 101001 01001
oder10100 1010101010101010 10100 10100 1
. Aber wenn wir einen beliebigen Wert damit multiplizieren, gibt es viele überlappende Übertragungen, sodass ich nicht sehen konnte, wie es funktioniert. Der Ausgang kann auch eine andere Anzahl von gesetzten Bits haben, so dass es sich nicht wirklich um eine Permutation handeltSie finden den anderen Generator in Roslyns AnonymousTypeGetHashCodeMethodSymbol, der die Konstante aufruft
HASH_FACTOR
Der wahre Grund für die Wahl dieses Wertes ist noch unklar
quelle
Wie GökhanKurt in den Kommentaren erklärte, ändert sich die Anzahl basierend auf den beteiligten Eigenschaftsnamen. Wenn Sie die Eigenschaft in umbenennen
Halue
, lautet die Nummer stattdessen 387336856. Ich hatte es mit verschiedenen Klassen versucht, aber nicht daran gedacht, das Anwesen umzubenennen.Gökhans Kommentar ließ mich seinen Zweck verstehen. Das Versetzen von Hashwerten basiert auf einem deterministischen, aber zufällig verteilten Versatz. Auf diese Weise ist das Kombinieren von Hash-Werten für verschiedene Klassen selbst mit einer einfachen Addition immer noch leicht resistent gegen Hash-Kollisionen.
Wenn Sie beispielsweise zwei Klassen mit ähnlichen GetHashCode-Implementierungen haben:
und wenn Sie eine andere Klasse haben, die Verweise auf diese beiden enthält:
Eine schlechte Kombination wie diese würde zu Hash-Kollisionen neigen, da sich der resultierende Hash-Code für verschiedene Werte von ValueA und ValueB im selben Bereich ansammeln würde, wenn ihre Werte nahe beieinander liegen. Es spielt wirklich keine Rolle, ob Sie Multiplikations- oder bitweise Operationen verwenden, um sie zu kombinieren. Sie sind dennoch anfällig für Kollisionen ohne gleichmäßig verteilten Versatz. Da viele bei der Programmierung verwendete ganzzahlige Werte um 0 akkumuliert werden, ist es sinnvoll, einen solchen Offset zu verwenden
Anscheinend ist es eine gute Praxis, einen zufälligen Versatz mit guten Bitmustern zu haben.
Ich bin mir immer noch nicht sicher, warum sie keine völlig zufälligen Offsets verwenden, wahrscheinlich um keinen Code zu beschädigen, der auf dem Determinismus von GetHashCode () beruht, aber es wäre großartig, einen Kommentar vom Visual Studio-Team dazu zu erhalten.
quelle