Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Dieser Artikel enthält ergänzende Hinweise zur Referenzdokumentation für diese API.
Die GetHashCode Methode stellt einen Hashcode für Algorithmen bereit, die schnelle Überprüfungen der Objektgleichheit benötigen. Ein Hashcode ist ein numerischer Wert, der zum Einfügen und Identifizieren eines Objekts in einer hashbasierten Auflistung verwendet wird, z. B. die Dictionary<TKey,TValue> Klasse, die Hashtable Klasse oder einen von der DictionaryBase Klasse abgeleiteten Typ.
Hinweis
Informationen dazu, wie Hashcodes in Hashtabellen und für einige zusätzliche Hashcodealgorithmen verwendet werden, finden Sie im Eintrag "Hash function " in Wikipedia.
Zwei Objekte, die gleich sind, erzeugen gleiche Hashcodes. Die Umgekehrte ist jedoch nicht wahr: Gleiche Hashcodes implizieren keine Objektgleichheit, da verschiedene (ungleiche) Objekte identische Hashcodes aufweisen können. Darüber hinaus garantiert .NET nicht die Standardimplementierung der GetHashCode Methode, und der Wert, den diese Methode zurückgibt, kann sich zwischen .NET-Implementierungen, wie z. B. verschiedenen Versionen von .NET Framework und .NET Core, und Plattformen wie 32-Bit- und 64-Bit-Plattformen unterscheiden. Verwenden Sie aus diesen Gründen die Standardimplementierung dieser Methode nicht als eindeutigen Objektbezeichner für Hashing-Zwecke. Daraus folgen zwei Folgen:
- Sie sollten nicht davon ausgehen, dass gleiche Hashcodes die Objektgleichheit implizieren.
- Sie sollten niemals einen Hashcode außerhalb der Anwendungsdomäne beibehalten oder verwenden, in der sie erstellt wurde, da dasselbe Objekt einen Hash über Anwendungsdomänen, Prozesse und Plattformen hinweg ausführen kann.
Warnung
Ein Hashcode dient zum effizienten Einfügen und Nachschlagen in Auflistungen, die auf einer Hashtabelle basieren. Ein Hashcode ist kein dauerhafter Wert. Aus diesem Grund:
- Serialisieren Sie keine Hashcodewerte, oder speichern Sie sie in Datenbanken.
- Verwenden Sie den Hashcode nicht als Schlüssel, um ein Objekt aus einer keyed-Auflistung abzurufen.
- Senden Sie Hashcodes nicht über Anwendungsdomänen oder Prozesse hinweg. In einigen Fällen können Hashcodes pro Prozess oder Anwendungsdomäne berechnet werden.
- Verwenden Sie nicht den Hashcode anstelle eines Werts, der von einer kryptografischen Hashfunktion zurückgegeben wird, wenn Sie einen kryptografisch starken Hash benötigen. Verwenden Sie für kryptografische Hashes eine von der System.Security.Cryptography.HashAlgorithm Oder-Klasse System.Security.Cryptography.KeyedHashAlgorithm abgeleitete Klasse.
- Vermeiden Sie es, die Gleichheit von Hashcodes zu prüfen, um festzustellen, ob zwei Objekte gleich sind. (Ungleiche Objekte können identische Hashcodes aufweisen.) Um die Gleichheit zu testen, rufen Sie die ReferenceEquals- oder Equals-Methode auf.
Die GetHashCode-Methode kann von einem abgeleiteten Typ überschrieben werden. Wenn GetHashCode nicht außer Kraft gesetzt wird, werden Hashcodes für Referenztypen durch Aufrufen der Object.GetHashCode Methode der Basisklasse berechnet, die einen Hashcode basierend auf dem Verweis eines Objekts berechnet. Weitere Informationen finden Sie unter RuntimeHelpers.GetHashCode. Mit anderen Worten, zwei Objekte, für die die Methode ReferenceEquals den Wert true
zurückgibt, haben identische Hashcodes. Wenn Wertetypen GetHashCode nicht überschreiben, verwendet die ValueType.GetHashCode-Methode der Basisklasse Reflection, um den Hash-Code auf der Grundlage der Werte der Felder des Typs zu berechnen. Mit anderen Worten: Werttypen, deren Felder gleich Werte haben, weisen gleiche Hashcodes auf. Weitere Informationen zum Überschreiben von GetHashCode, finden Sie im Abschnitt "Hinweise für Erben".
Warnung
Wenn Sie die GetHashCode Methode überschreiben, sollten Sie auch die Equals Methode überschreiben und umgekehrt. Wenn Ihre überschreibende Equals-Methode true
zurückgibt, wenn zwei Objekte auf Gleichheit getestet werden, muss Ihre überschreibende GetHashCode-Methode denselben Wert für die beiden Objekte zurückgeben.
Wenn ein Objekt, das als Schlüssel in einer Hashtabelle verwendet wird, keine nützliche Implementierung GetHashCodebereitstellt, können Sie einen Hashcodeanbieter angeben, indem Sie eine IEqualityComparer Implementierung für eine der Überladungen des Hashtable Klassenkonstruktors bereitstellen.
Hinweise für die Windows-Runtime
Wenn Sie die GetHashCode Methode für eine Klasse in der Windows-Runtime aufrufen, stellt sie das Standardverhalten für Klassen bereit, die nicht außer Kraft setzen GetHashCode. Dies ist Teil der Unterstützung, die .NET für die Windows-Runtime bereitstellt (siehe .NET-Support für Windows Store-Apps und Windows-Runtime). Klassen in der Windows-Runtime erben Object nicht und implementieren derzeit keinen GetHashCode. Jedoch scheinen sie über ToString, Equals(Object), und GetHashCode Methoden zu verfügen, wenn Sie sie in Ihrem C#- oder Visual Basic-Code verwenden, und das .NET Framework liefert das Standardverhalten für diese Methoden.
Hinweis
Windows-Runtime-Klassen, die in C# oder Visual Basic geschrieben wurden, können die GetHashCode Methode überschreiben.
Beispiele
Eine der einfachsten Methoden zum Berechnen eines Hashcodes für einen numerischen Wert mit demselben oder einem kleineren Bereich als der Int32 Typ besteht darin, einfach diesen Wert zurückzugeben. Das folgende Beispiel zeigt eine solche Implementierung für eine Number
Struktur.
using System;
public struct Number
{
private int n;
public Number(int value)
{
n = value;
}
public int Value
{
get { return n; }
}
public override bool Equals(Object obj)
{
if (obj == null || ! (obj is Number))
return false;
else
return n == ((Number) obj).n;
}
public override int GetHashCode()
{
return n;
}
public override string ToString()
{
return n.ToString();
}
}
public class Example1
{
public static void Main()
{
Random rnd = new Random();
for (int ctr = 0; ctr <= 9; ctr++) {
int randomN = rnd.Next(Int32.MinValue, Int32.MaxValue);
Number n = new Number(randomN);
Console.WriteLine("n = {0,12}, hash code = {1,12}", n, n.GetHashCode());
}
}
}
// The example displays output like the following:
// n = -634398368, hash code = -634398368
// n = 2136747730, hash code = 2136747730
// n = -1973417279, hash code = -1973417279
// n = 1101478715, hash code = 1101478715
// n = 2078057429, hash code = 2078057429
// n = -334489950, hash code = -334489950
// n = -68958230, hash code = -68958230
// n = -379951485, hash code = -379951485
// n = -31553685, hash code = -31553685
// n = 2105429592, hash code = 2105429592
open System
[<Struct; CustomEquality; NoComparison>]
type Number(value: int) =
member _.Value = value
override _.Equals(obj) =
match obj with
| :? Number as n ->
n.Value = value
| _ -> false
override _.GetHashCode() =
value
override _.ToString() =
string value
let rnd = Random()
for _ = 0 to 9 do
let randomN = rnd.Next(Int32.MinValue, Int32.MaxValue)
let n = Number randomN
printfn $"n = {n,12}, hash code = {n.GetHashCode(),12}"
// The example displays output like the following:
// n = -634398368, hash code = -634398368
// n = 2136747730, hash code = 2136747730
// n = -1973417279, hash code = -1973417279
// n = 1101478715, hash code = 1101478715
// n = 2078057429, hash code = 2078057429
// n = -334489950, hash code = -334489950
// n = -68958230, hash code = -68958230
// n = -379951485, hash code = -379951485
// n = -31553685, hash code = -31553685
// n = 2105429592, hash code = 2105429592
Public Structure Number
Private n As Integer
Public Sub New(value As Integer)
n = value
End Sub
Public ReadOnly Property Value As Integer
Get
Return n
End Get
End Property
Public Overrides Function Equals(obj As Object) As Boolean
If obj Is Nothing OrElse Not TypeOf obj Is Number Then
Return False
Else
Return n = CType(obj, Number).n
End If
End Function
Public Overrides Function GetHashCode() As Integer
Return n
End Function
Public Overrides Function ToString() As String
Return n.ToString()
End Function
End Structure
Module Example1
Public Sub Main()
Dim rnd As New Random()
For ctr As Integer = 0 To 9
Dim randomN As Integer = rnd.Next(Int32.MinValue, Int32.MaxValue)
Dim n As New Number(randomN)
Console.WriteLine("n = {0,12}, hash code = {1,12}", n, n.GetHashCode())
Next
End Sub
End Module
' The example displays output like the following:
' n = -634398368, hash code = -634398368
' n = 2136747730, hash code = 2136747730
' n = -1973417279, hash code = -1973417279
' n = 1101478715, hash code = 1101478715
' n = 2078057429, hash code = 2078057429
' n = -334489950, hash code = -334489950
' n = -68958230, hash code = -68958230
' n = -379951485, hash code = -379951485
' n = -31553685, hash code = -31553685
' n = 2105429592, hash code = 2105429592
Häufig verfügt ein Typ über mehrere Datenfelder, die an der Generierung des Hashcodes teilnehmen können. Eine Möglichkeit zum Generieren eines Hashcodes besteht darin, diese Felder mithilfe eines XOR (eXclusive OR)
Vorgangs zu kombinieren, wie im folgenden Beispiel gezeigt.
using System;
// A type that represents a 2-D point.
public struct Point2
{
private int x;
private int y;
public Point2(int x, int y)
{
this.x = x;
this.y = y;
}
public override bool Equals(Object obj)
{
if (! (obj is Point2)) return false;
Point2 p = (Point2) obj;
return x == p.x & y == p.y;
}
public override int GetHashCode()
{
return x ^ y;
}
}
public class Example3
{
public static void Main()
{
Point2 pt = new Point2(5, 8);
Console.WriteLine(pt.GetHashCode());
pt = new Point2(8, 5);
Console.WriteLine(pt.GetHashCode());
}
}
// The example displays the following output:
// 13
// 13
// A type that represents a 2-D point.
[<Struct; CustomEquality; NoComparison>]
type Point(x: int, y: int) =
member _.X = x
member _.Y = y
override _.Equals(obj) =
match obj with
| :? Point as p ->
x = p.X && y = p.Y
| _ ->
false
override _.GetHashCode() =
x ^^^ y
let pt = Point(5, 8)
printfn $"{pt.GetHashCode()}"
let pt2 = Point(8, 5)
printfn $"{pt.GetHashCode()}"
// The example displays the following output:
// 13
// 13
' A type that represents a 2-D point.
Public Structure Point3
Private x As Integer
Private y As Integer
Public Sub New(x As Integer, y As Integer)
Me.x = x
Me.y = y
End Sub
Public Overrides Function Equals(obj As Object) As Boolean
If Not TypeOf obj Is Point3 Then Return False
Dim p As Point3 = CType(obj, Point3)
Return x = p.x And y = p.y
End Function
Public Overrides Function GetHashCode() As Integer
Return x Xor y
End Function
End Structure
Public Module Example3
Public Sub Main()
Dim pt As New Point3(5, 8)
Console.WriteLine(pt.GetHashCode())
pt = New Point3(8, 5)
Console.WriteLine(pt.GetHashCode())
End Sub
End Module
Im vorherigen Beispiel wird derselbe Hashcode für (n1, n2) und (n2, n1) zurückgegeben, sodass möglicherweise mehr Kollisionen generiert werden, als wünschenswert sind. Eine Reihe von Lösungen ist verfügbar, sodass Hashcodes in diesen Fällen nicht identisch sind. Eine besteht darin, den Hashcode eines Tuple
Objekts zurückzugeben, das die Reihenfolge jedes Felds widerspiegelt. Das folgende Beispiel zeigt eine mögliche Implementierung, die die Tuple<T1,T2> Klasse verwendet. Beachten Sie jedoch, dass sich der Leistungsaufwand beim Instanziieren eines Tuple
Objekts erheblich auf die Gesamtleistung einer Anwendung auswirken kann, die große Anzahl von Objekten in Hashtabellen speichert.
using System;
public struct Point3
{
private int x;
private int y;
public Point3(int x, int y)
{
this.x = x;
this.y = y;
}
public override bool Equals(Object obj)
{
if (obj is Point3)
{
Point3 p = (Point3) obj;
return x == p.x & y == p.y;
}
else
{
return false;
}
}
public override int GetHashCode()
{
return Tuple.Create(x, y).GetHashCode();
}
}
public class Example
{
public static void Main()
{
Point3 pt = new Point3(5, 8);
Console.WriteLine(pt.GetHashCode());
pt = new Point3(8, 5);
Console.WriteLine(pt.GetHashCode());
}
}
// The example displays the following output:
// 173
// 269
[<Struct; CustomEquality; NoComparison>]
type Point(x: int, y: int) =
member _.X = x
member _.Y = y
override _.Equals(obj) =
match obj with
| :? Point as p ->
x = p.X && y = p.Y
| _ ->
false
override _.GetHashCode() =
(x, y).GetHashCode()
let pt = Point(5, 8)
printfn $"{pt.GetHashCode()}"
let pt2 = Point(8, 5)
printfn $"{pt2.GetHashCode()}"
// The example displays the following output:
// 173
// 269
Public Structure Point
Private x As Integer
Private y As Integer
Public Sub New(x As Integer, y As Integer)
Me.x = x
Me.y = y
End Sub
Public Overrides Function Equals(obj As Object) As Boolean
If Not TypeOf obj Is Point Then Return False
Dim p As Point = CType(obj, Point)
Return x = p.x And y = p.y
End Function
Public Overrides Function GetHashCode() As Integer
Return Tuple.Create(x, y).GetHashCode()
End Function
End Structure
Public Module Example
Public Sub Main()
Dim pt As New Point(5, 8)
Console.WriteLine(pt.GetHashCode())
pt = New Point(8, 5)
Console.WriteLine(pt.GetHashCode())
End Sub
End Module
' The example displays the following output:
' 173
' 269
Eine zweite alternative Lösung umfasst die Gewichtung der einzelnen Hashcodes durch das Linksverschieben der Hashcodes aufeinanderfolgender Felder um zwei oder mehr Bits. Optimalerweise sollten Bits, die über Bit 31 hinaus verschoben werden, nicht verworfen werden, sondern umlaufen. Da die Bits sowohl in C# als auch in Visual Basic von den Left-Shift-Operatoren verworfen werden, muss dazu eine Left-Shift-and-Wrap-Methode wie die folgende erstellt werden:
public int ShiftAndWrap(int value, int positions)
{
positions = positions & 0x1F;
// Save the existing bit pattern, but interpret it as an unsigned integer.
uint number = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
// Preserve the bits to be discarded.
uint wrapped = number >> (32 - positions);
// Shift and wrap the discarded bits.
return BitConverter.ToInt32(BitConverter.GetBytes((number << positions) | wrapped), 0);
}
let shiftAndWrap (value: int) positions =
let positions = positions &&& 0x1F
// Save the existing bit pattern, but interpret it as an unsigned integer.
let number = BitConverter.ToUInt32(BitConverter.GetBytes value, 0)
// Preserve the bits to be discarded.
let wrapped = number >>> (32 - positions)
// Shift and wrap the discarded bits.
BitConverter.ToInt32(BitConverter.GetBytes((number <<< positions) ||| wrapped), 0)
Public Function ShiftAndWrap(value As Integer, positions As Integer) As Integer
positions = positions And &h1F
' Save the existing bit pattern, but interpret it as an unsigned integer.
Dim number As UInteger = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0)
' Preserve the bits to be discarded.
Dim wrapped AS UInteger = number >> (32 - positions)
' Shift and wrap the discarded bits.
Return BitConverter.ToInt32(BitConverter.GetBytes((number << positions) Or wrapped), 0)
End Function
Das folgende Beispiel verwendet diese Shift-and-Wrap-Methode, um den Code der Point
-Struktur zu berechnen, die in den vorherigen Beispielen verwendet wurde.
using System;
public struct Point
{
private int x;
private int y;
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
public override bool Equals(Object obj)
{
if (!(obj is Point)) return false;
Point p = (Point) obj;
return x == p.x & y == p.y;
}
public override int GetHashCode()
{
return ShiftAndWrap(x.GetHashCode(), 2) ^ y.GetHashCode();
}
private int ShiftAndWrap(int value, int positions)
{
positions = positions & 0x1F;
// Save the existing bit pattern, but interpret it as an unsigned integer.
uint number = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
// Preserve the bits to be discarded.
uint wrapped = number >> (32 - positions);
// Shift and wrap the discarded bits.
return BitConverter.ToInt32(BitConverter.GetBytes((number << positions) | wrapped), 0);
}
}
public class Example2
{
public static void Main()
{
Point pt = new Point(5, 8);
Console.WriteLine(pt.GetHashCode());
pt = new Point(8, 5);
Console.WriteLine(pt.GetHashCode());
}
}
// The example displays the following output:
// 28
// 37
open System
[<Struct; CustomEquality; NoComparison>]
type Point(x: int, y: int) =
member _.X = x
member _.Y = y
override _.Equals(obj) =
match obj with
| :? Point as p ->
x = p.X && y = p.Y
| _ ->
false
override this.GetHashCode() =
this.ShiftAndWrap(x.GetHashCode(), 2) ^^^ y.GetHashCode()
member _.ShiftAndWrap(value, positions) =
let positions = positions &&& 0x1F
// Save the existing bit pattern, but interpret it as an unsigned integer.
let number = BitConverter.ToUInt32(BitConverter.GetBytes value, 0)
// Preserve the bits to be discarded.
let wrapped = number >>> (32 - positions)
// Shift and wrap the discarded bits.
BitConverter.ToInt32(BitConverter.GetBytes((number <<< positions) ||| wrapped), 0)
let pt = Point(5, 8)
printfn $"{pt.GetHashCode()}"
let pt2 = Point(8, 5)
printfn $"{pt2.GetHashCode()}"
// The example displays the following output:
// 28
// 37
Public Structure Point5
Private x As Integer
Private y As Integer
Public Sub New(x As Integer, y As Integer)
Me.x = x
Me.y = y
End Sub
Public Overrides Function Equals(obj As Object) As Boolean
If Not TypeOf obj Is Point5 Then Return False
Dim p As Point5 = CType(obj, Point5)
Return x = p.x And y = p.y
End Function
Public Overrides Function GetHashCode() As Integer
Return ShiftAndWrap(x.GetHashCode(), 2) Xor y.GetHashCode()
End Function
Private Function ShiftAndWrap(value As Integer, positions As Integer) As Integer
positions = positions And &H1F
' Save the existing bit pattern, but interpret it as an unsigned integer.
Dim number As UInteger = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0)
' Preserve the bits to be discarded.
Dim wrapped As UInteger = number >> (32 - positions)
' Shift and wrap the discarded bits.
Return BitConverter.ToInt32(BitConverter.GetBytes((number << positions) Or wrapped), 0)
End Function
End Structure
Module Example2
Public Sub Main()
Dim pt As New Point5(5, 8)
Console.WriteLine(pt.GetHashCode())
pt = New Point5(8, 5)
Console.WriteLine(pt.GetHashCode())
End Sub
End Module
' The example displays the following output:
' 28
' 37