Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Anmärkning
Den här artikeln innehåller ytterligare kommentarer till referensdokumentationen för det här API:et.
Metoden GetHashCode tillhandahåller en hash-kod för algoritmer som behöver snabbkontroller av objektjämlikhet. En hash-kod är ett numeriskt värde som används för att infoga och identifiera ett objekt i en hash-baserad samling, till exempel Dictionary<TKey,TValue> klassen, Hashtable klassen eller en typ som härletts från DictionaryBase klassen.
Anmärkning
Information om hur hashkoder används i hash-tabeller och för ytterligare hashkodalgoritmer finns i hash-funktionsposten i Wikipedia.
Två objekt som är lika returnerar hashkoder som också är lika. Det omvända är dock inte sant: lika hashkoder innebär inte objektjämlikhet, eftersom olika (ojämlika) objekt kan ha identiska hash-koder. Dessutom garanterar .NET inte standardimplementeringen av GetHashCode metoden, och värdet som den här metoden returnerar kan skilja sig mellan .NET-implementeringar, till exempel olika versioner av .NET Framework och .NET Core och plattformar, till exempel 32-bitars- och 64-bitarsplattformar. Av dessa skäl ska du inte använda standardimplementeringen av den här metoden som ett unikt objektidentifierare för hashningsändamål. Två konsekvenser följer av detta:
- Du bör inte anta att lika hashkoder innebär objektjämlikhet.
- Du bör aldrig bevara eller använda en hash-kod utanför programdomänen där den skapades, eftersom samma objekt kan hasha mellan programdomäner, processer och plattformar.
Varning
En hash-kod är avsedd för effektiv infogning och sökning i samlingar som baseras på en hash-tabell. En hash-kod är inte ett permanent värde. Av den anledningen:
- Serialisera inte hash-kodvärden eller lagra dem i databaser.
- Använd inte hash-koden som nyckel för att hämta ett objekt från en nyckelsamling.
- Skicka inte hash-koder mellan programdomäner eller processer. I vissa fall kan hashkoder beräknas per process eller per programdomän.
- Använd inte hashkoden i stället för ett värde som returneras av en kryptografisk hashfunktion om du behöver en kryptografiskt stark hash. För kryptografiska hashar använder du en klass som härletts från System.Security.Cryptography.HashAlgorithm klassen eller System.Security.Cryptography.KeyedHashAlgorithm .
- Testa inte för likhet mellan hashkoder för att avgöra om två objekt är lika. (Ojämlika objekt kan ha identiska hash-koder.) För att testa jämlikhet, anropa metoden ReferenceEquals eller Equals.
Metoden GetHashCode kan åsidosättas av en härledd typ. Om GetHashCode inte åsidosätts beräknas hash-koder för referenstyper genom att anropa Object.GetHashCode metoden för basklassen, som beräknar en hash-kod baserat på ett objekts referens. Mer information RuntimeHelpers.GetHashCodefinns i . Med andra ord har två objekt som ReferenceEquals metoden returnerar true identiska hash-koder för. Om värdetyper inte åsidosätter GetHashCodeValueType.GetHashCode använder metoden för basklassen reflektion för att beräkna hash-koden baserat på värdena för typens fält. Med andra ord har värdetyper vars fält har lika värden lika med hashkoder. Mer information om åsidosättande av GetHashCode finns i avsnittet "Anteckningar till arvtagare".
Varning
Om du åsidosätter GetHashCode metoden bör du också åsidosätta Equals, och vice versa. Om den åsidosatta Equals metoden returnerar true när två objekt testas för likhet måste den åsidosatta GetHashCode metoden returnera samma värde för de två objekten.
Om ett objekt som används som en nyckel i en hash-tabell inte ger någon användbar implementering av GetHashCodekan du ange en hash-kodprovider genom att ange en IEqualityComparer implementering till en av överlagringarna av Hashtable klasskonstruktorn.
Anteckningar för Windows Runtime
När du anropar metoden GetHashCode på en klass i Windows Runtime, tillhandahåller den standardbeteendet för klasser som inte åsidosätter GetHashCode. Detta är en del av det stöd som .NET tillhandahåller för Windows Runtime (se .NET-stöd för Windows Store-appar och Windows Runtime). Klasser i Windows Runtime ärver inte Object och implementerar för närvarande inte en GetHashCode. De verkar dock ha ToStringmetoder för , Equals(Object)och GetHashCode när du använder dem i C# eller Visual Basic-koden, och .NET Framework tillhandahåller standardbeteendet för dessa metoder.
Anmärkning
Windows Runtime-klasser som är skrivna i C# eller Visual Basic kan åsidosätta metoden GetHashCode.
Exempel
Ett av de enklaste sätten Int32 att beräkna en hashkod för ett numeriskt värde som har samma eller ett mindre intervall än typen är att helt enkelt returnera det värdet. I följande exempel visas en sådan implementering för en 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
Ofta har en typ flera datafält som kan delta i genereringen av hash-koden. Ett sätt att generera en hashkod är att kombinera dessa fält med hjälp av en XOR (eXclusive OR) åtgärd, som du ser i följande exempel.
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
I föregående exempel returneras samma hashkod för (n1, n2) och (n2, n1), så det kan generera fler kollisioner än vad som är önskvärt. På .NET 5+ är den rekommenderade lösningen att använda HashCode.Combine. Det undviker symmetriproblemet och skapar en välfördelad hashkod utan att behöva skapa objektet Tuple.
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 HashCode.Combine(x, y);
}
}
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 output similar to the following.
// Note: HashCode.Combine results are not stable across .NET versions.
// 185727722
// -363254492
[<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() =
System.HashCode.Combine(x, y)
let pt = Point(5, 8)
printfn $"{pt.GetHashCode()}"
let pt2 = Point(8, 5)
printfn $"{pt2.GetHashCode()}"
// The example displays output similar to the following.
// Note: HashCode.Combine results are not stable across .NET versions.
// 185727722
// -363254492
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 HashCode.Combine(x, y)
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 output similar to the following.
' Note: HashCode.Combine results are not stable across .NET versions.
' 185727722
' -363254492
I .NET Framework är ett alternativ att vikta de enskilda hash-koderna genom att vänsterförskjuta hash-koderna för efterföljande fält med två eller flera bitar. Idealiskt bör bitar som flyttas bortom bit 31 rulla runt i stället för att kasseras. Eftersom bitar ignoreras av vänsterskiftsoperatorerna i både C# och Visual Basic måste du skapa en vänster shift-and-wrap-metod som följande:
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
I följande exempel används sedan den här metoden shift-and-wrap för att beräkna hashkoden för den Point struktur som användes i föregående exempel.
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