Object.GetHashCode Metoda
Definice
Důležité
Některé informace platí pro předběžně vydaný produkt, který se může zásadně změnit, než ho výrobce nebo autor vydá. Microsoft neposkytuje žádné záruky, výslovné ani předpokládané, týkající se zde uváděných informací.
Slouží jako výchozí funkce hash.
public:
virtual int GetHashCode();
public virtual int GetHashCode ();
abstract member GetHashCode : unit -> int
override this.GetHashCode : unit -> int
Public Overridable Function GetHashCode () As Integer
Návraty
Kód hash pro aktuální objekt.
Příklady
Jedním z nejjednodušších způsobů, jak vypočítat kód hash pro číselnou hodnotu, která má stejný nebo menší rozsah než Int32 typ, je jednoduše vrátit danou hodnotu. Následující příklad ukazuje takovou implementaci struktury Number
.
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 Example
{
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 Example
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
Často má typ více datových polí, která se můžou účastnit generování kódu hash. Jedním ze způsobů, jak vygenerovat kód hash, je kombinovat tato pole pomocí XOR (eXclusive OR)
operace, jak je znázorněno v následujícím příkladu.
using System;
// A type that represents a 2-D point.
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 x ^ y;
}
}
public class Example
{
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:
// 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 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 x Xor 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
Předchozí příklad vrátí stejný kód hash pro (n1, n2) a (n2, n1) a tak může generovat více kolizí, než je žádoucí. K dispozici je řada řešení, aby kódy hash v těchto případech nebyly identické. Jedním z nich je vrácení hash kódu objektu Tuple
, který odpovídá pořadí jednotlivých polí. Následující příklad ukazuje možnou Tuple<T1,T2> implementaci, která používá třídu. Všimněte si ale, že režie výkonu vytvoření instance Tuple
objektu může výrazně ovlivnit celkový výkon aplikace, která ukládá velký počet objektů v tabulkách hash.
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)
{
Point p = (Point) 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()
{
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:
// 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
Druhým alternativním řešením je váhování jednotlivých kódů hash posunutím kódů hash po sobě jdoucích polí o dvě nebo více bitů. Optimální je, že by se bity posunuté za bit 31 měly zabalit místo toho, aby se zahodily. Vzhledem k tomu, že operátory levého posunu v jazyce C# i Visual Basic zahodí bity, je potřeba vytvořit metodu levého posunu a obtékání, například následující:
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
Následující příklad pak použije tuto metodu shift-and-wrap k výpočtu hash kódu Point
struktury použité v předchozích příkladech.
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 Example
{
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 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 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 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:
' 28
' 37
Poznámky
Kód hash je číselná hodnota, která slouží k vložení a identifikaci objektu v kolekci založené na hodnotě hash, jako Dictionary<TKey,TValue> je třída, Hashtable třída nebo typ odvozený z DictionaryBase třídy. Metoda GetHashCode poskytuje tento hash kód pro algoritmy, které potřebují rychlé kontroly rovnosti objektů.
Poznámka
Informace o tom, jak se kódy hash používají v tabulkách hash a u některých dalších algoritmů hash kódu, najdete v položce funkce hash na Wikipedii.
Dva objekty, které jsou stejné, vrací kódy hash, které jsou stejné. Obrácená hodnota však není pravdivá: stejné hashové kódy neznamená rovnost objektů, protože různé (nerovné) objekty mohou mít stejné hashové kódy. Navíc .NET nezaručuje výchozí implementaci GetHashCode metody a hodnota, kterou tato metoda vrací, se může lišit mezi implementacemi .NET, jako jsou různé verze .NET Framework a .NET Core a platformy, jako jsou 32bitové a 64bitové platformy. Z těchto důvodů nepoužívejte výchozí implementaci této metody jako jedinečný identifikátor objektu pro účely hash. Z toho vyplývá dva důsledky:
Neměli byste předpokládat, že stejné kódy hash znamenají rovnost objektů.
Nikdy byste neměli uchovávat nebo používat kód hash mimo doménu aplikace, ve které byl vytvořen, protože stejný objekt může hashovat mezi doménami aplikací, procesy a platformami.
Upozornění
Kód hash je určený pro efektivní vkládání a vyhledávání v kolekcích založených na tabulce hash. Kód hash není trvalou hodnotou. Z tohoto důvodu:
- Nevyužívejte hodnoty kódu hash ani je neukládáte do databází.
- Nepoužívejte kód hash jako klíč k načtení objektu z kolekce klíčů.
- Neodesílejte kódy hash napříč doménami nebo procesy aplikací. V některých případech se kódy hash můžou vypočítat na základě jednotlivých procesů nebo domény aplikace.
- Nepoužívejte kód hash místo hodnoty vrácené kryptografickou hashovací funkcí, pokud potřebujete kryptograficky silnou hodnotu hash. Pro kryptografické hodnoty hash použijte třídu odvozenou z System.Security.Cryptography.HashAlgorithm třídy nebo System.Security.Cryptography.KeyedHashAlgorithm třídy.
- Neotestujte rovnost kódů hash, abyste zjistili, jestli jsou dva objekty stejné. (Nerovné objekty můžou mít identické kódy hash.) Pokud chcete otestovat rovnost, zavolejte metodu nebo Equals metoduReferenceEquals.
Metodu GetHashCode lze přepsat odvozeným typem. Pokud GetHashCode není přepsán, kódy hash pro odkazové typy jsou vypočítávány voláním Object.GetHashCode metody základní třídy, která vypočítá hash kód založený na odkazu objektu. Další informace najdete v tématu RuntimeHelpers.GetHashCode. Jinými slovy, dva objekty, pro které ReferenceEquals metoda vrací true
, mají stejné hashové kódy. Pokud typy hodnot nepřepíší GetHashCode, ValueType.GetHashCode metoda základní třídy používá reflexi k výpočtu kódu hash na základě hodnot polí typu. Jinými slovy, typy hodnot, jejichž pole mají stejné hodnoty, mají stejné hashové kódy. Další informace o přepsání GetHashCodenajdete v části Poznámky k dědění.
Upozornění
Pokud metodu GetHashCode přepíšete, měli byste ji také přepsat Equalsa naopak. Pokud se vaše přepsaná Equals metoda vrátí true
, když jsou testovány dva objekty pro rovnost, musí metoda přepsání GetHashCode vrátit stejnou hodnotu pro tyto dva objekty.
Pokud objekt, který se používá jako klíč v tabulce hash, neposkytuje užitečnou implementaci GetHashCode, můžete zadat zprostředkovatele kódu hash zadáním IEqualityComparer implementace do jednoho z přetížení konstruktoru Hashtable třídy.
Poznámky pro prostředí Windows Runtime
Když zavoláte metodu GetHashCode třídy v prostředí Windows Runtime, poskytuje výchozí chování tříd, které nepřepíší GetHashCode. Tato podpora je součástí podpory, kterou .NET Framework poskytuje prostředí Windows Runtime (viz .NET Framework Podpora aplikací pro Windows Store a prostředí Windows Runtime). Třídy v prostředí Windows Runtime nedědí Objecta aktuálně neimplementují GetHashCode. Zdá se ale, že mají ToString, Equals(Object)a GetHashCode metody, když je použijete v kódu C# nebo Visual Basic a .NET Framework poskytuje výchozí chování pro tyto metody.
Poznámka
prostředí Windows Runtime třídy napsané v jazyce C# nebo Visual Basic mohou metodu GetHashCode přepsat.
Poznámky pro dědice
Funkce hash se používá k rychlému vygenerování čísla (hashového kódu), která odpovídá hodnotě objektu. Funkce hash jsou obvykle specifické pro každý typ a pro jedinečnost musí jako vstup použít alespoň jedno z polí instance. Kódy hash by se neměly vypočítat pomocí hodnot statických polí.
Pro třídy odvozené z Object, GetHashCode
metoda může delegovat na implementaci základní třídy GetHashCode() pouze v případě, že odvozená třída definuje rovnost, aby byla referenční rovnost. Výchozí implementace GetHashCode() referenčních typů vrátí kód hash, který odpovídá hodnotě, která je vrácené metodou GetHashCode(Object) . U neměnných referenčních typů můžete přepsat GetHashCode() . Obecně platí, že u ztlumitelných referenčních typů byste měli přepsat GetHashCode() pouze v případě, že:
– Kód hash můžete vypočítat z polí, která nejsou ztlumitelná; Nebo
- Můžete zajistit, aby se kód hash z mutable objektu nezměnil, když je objekt obsažený v kolekci, která spoléhá na jeho hashový kód.
Jinak si můžete myslet, že v tabulce hash dojde ke ztrátě mutable objektu. Pokud se rozhodnete přepsat u ztlumitelného GetHashCode() referenčního typu, měla by dokumentace smazat, že uživatelé vašeho typu by neměli upravovat hodnoty objektů, zatímco je objekt uložený v tabulce hash.
Pro typy GetHashCode() hodnot poskytuje výchozí implementaci kódu hash, která používá reflexi. Měli byste zvážit jeho přepsání pro lepší výkon.
Další informace a příklady výpočetních kódů hash různými způsoby najdete v části Příklady.
Funkce hash musí mít následující vlastnosti:
- Pokud se dva objekty porovnávají stejně, GetHashCode() musí metoda pro každý objekt vrátit stejnou hodnotu. Pokud se ale dva objekty nerovnají, GetHashCode() nemusí metody pro tyto dva objekty vracet různé hodnoty.
GetHashCode()- Metoda objektu musí konzistentně vrátit stejný hash kód, pokud neexistuje žádná úprava stavu objektu, který určuje návratovou hodnotu metody System.Object.Equals objektu. Všimněte si, že platí pouze pro aktuální spuštění aplikace a že se dá vrátit jiný kód hash, pokud se aplikace spustí znovu.
Pro nejlepší výkon by funkce hash měla vygenerovat sudou distribuci pro všechny vstupy, včetně vstupu, který je silně clusterovaný. Implicace spočívá v tom, že malé úpravy stavu objektu by měly vést k velkým úpravám výsledného hashového kódu za účelem dosažení nejlepšího výkonu tabulky hash.
Funkce hash by měly být levné pro výpočet.
– Metoda GetHashCode() by neměla vyvolat výjimky.
Například implementace GetHashCode() metody poskytované String třídou vrací stejné hashové kódy pro stejné řetězcové hodnoty. Dva String objekty proto vrátí stejný kód hash, pokud představují stejnou řetězcovou hodnotu. Metoda také používá všechny znaky v řetězci k vygenerování přiměřeně náhodně distribuovaného výstupu, i když je vstup clusterovaný v určitých oblastech (například mnoho uživatelů může mít řetězce, které obsahují pouze nižší 128 znaků ASCII, i když řetězec může obsahovat libovolný z 65 535 znaků Unicode).
Poskytnutí dobré funkce hash třídy může výrazně ovlivnit výkon přidávání těchto objektů do tabulky hash. V tabulce hash s klíči, které poskytují dobrou implementaci funkce hash, hledání prvku trvá konstantní čas (například operace O(1). V tabulce hash s špatnou implementací funkce hash závisí výkon hledání na počtu položek v tabulce hash (například operace O(n
), kde n
je počet položek v tabulce hash. Uživatel se zlými úmysly může zadávat data, která zvyšují počet kolizí, což může výrazně snížit výkon aplikací závislých na tabulkách hash za následujících podmínek:
– Když funkce hash vytvářejí časté kolize.
Pokud velký podíl objektů v tabulce hash vytváří kódy hash, které jsou stejné nebo přibližně stejné navzájem.
Když uživatelé zadávají data, ze kterých se vypočítá kód hash.
Odvozené třídy, které přepíší, musí také přepsat GetHashCode() Equals(Object) , aby se zajistilo, že dva objekty, které jsou považovány za stejné, Hashtable mají stejný hash kód. V opačném případě nemusí typ správně fungovat.