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 tuto 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 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
Typ má často více datových polí, která se mohou účastnit generování kódu hash. Jedním ze způsobů, jak vygenerovat hashovací kód, 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 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 $"{pt2.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
Předchozí příklad vrátí stejný hashovací kód pro (n1, n2) a (n2, n1), a proto může generovat více kolizí, než je žádoucí. V .NET 5 nebo novějším doporučujeme použít HashCode.Combine. Vyhne se problému se symetrií a vytvoří dobře distribuovaný kód hash bez režie při vytváření objektu 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
Poznámky
Metoda GetHashCode poskytuje kód hash pro algoritmy, které potřebují rychlé kontroly rovnosti objektů. Kód hash je číselná hodnota, která slouží k vložení a identifikaci objektu v kolekci založené na hodnotě hash, jako třída Dictionary<TKey,TValue>, třída Hashtable nebo typ odvozený z třídy DictionaryBase.
Note
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 si rovny, vrací stejné hash kódy. Opačná hodnota však není pravdivá: stejné hashovací kódy neznamenají rovnost objektů, protože různé (nerovné) objekty mohou mít stejné hashovací kódy. Kromě toho .NET nezaručuje výchozí implementaci metody GetHashCode a hodnota, kterou tato metoda vrátí, se může lišit mezi implementacemi .NET a platformami, 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. Následují dva důsledky:
- Neměli byste předpokládat, že stejné hashovací kódy znamenají rovnost objektů.
- Nikdy byste neměli uchovávat ani používat kód hash mimo doménu aplikace, ve které byl vytvořen, protože stejný objekt může zatřiďovat hodnoty hash napříč doménami aplikace, procesy a platformami.
Warning
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:
- Neserializujte hodnoty kódu hash ani je neukládejte 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 aplikace. V některých případech mohou být hash kódy vytvářeny na základě jednotlivých procesů nebo aplikačních domén.
- 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 hodnot hash kódů, abyste zjistili, zda jsou dva objekty stejné. cs-CZ: (Nerovné objekty můžou mít identické kódy hash.) Chcete-li otestovat rovnost, zavolejte metodu ReferenceEquals nebo Equals.
Metodu GetHashCode lze přepsat odvozeným typem. Pokud GetHashCode není přepsán, kódy hash pro odkazové typy se počítají voláním Object.GetHashCode metody základní třídy, která vypočítá kód hash založený na odkazu objektu; více informací naleznete v RuntimeHelpers.GetHashCode. Jinými slovy, dva objekty, pro které ReferenceEquals metoda vrací true , mají stejné hashové kódy. Pokud typy hodnot nepřepíší GetHashCode, metoda ValueType.GetHashCode základní třídy používá reflexi k výpočtu hash kódu na základě hodnot polí typu. Jinými slovy, typy hodnot, jejichž pole mají stejné hodnoty, mají stejné hashovací kódy. Další informace o přepsání GetHashCodenaleznete v části Poznámky k dědění.
Warning
Pokud přepíšete metodu GetHashCode, měli byste také přepsat Equals, a naopak. Pokud přepsaná metoda Equals vrátí true při testování rovnosti dvou objektů, přepsaná metoda GetHashCode musí vrátit stejnou hodnotu pro oba objekty.
Pokud objekt, který se používá jako klíč v tabulce hash, neposkytuje užitečnou implementaci GetHashCode, můžete určit poskytovatele hashovacího kódu zadáním IEqualityComparer implementace pro jedno z přetížení konstruktoru třídy Hashtable.
Poznámky pro dědice
Funkce hash se používá k rychlému vygenerování čísla (kódu hash), 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 být referenční rovnost. Výchozí implementace pro odkazové GetHashCode() typy vrátí hash kód, který je ekvivalentní tomu, který vrací GetHashCode(Object) metoda. U neměnných typů odkazů můžete přepsat GetHashCode() . Obecně platí, že pro proměnlivé odkazové typy byste měli přepsat GetHashCode() pouze v případě, že:
Kód hash můžete vypočítat z polí, která nejsou proměnlivá. Nebo
Můžete zajistit, aby se kód hash proměnlivého objektu nezměnil, když je objekt obsažen v kolekci, která spoléhá na jeho hashovací kód.
V opačném případě se můžete zamyslet nad ztrátou proměnlivého objektu v tabulce hash. Pokud se rozhodnete přepsat GetHashCode() pro proměnlivý odkazový typ, měla by vaše dokumentace jasně určit, ž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, které počítají kódy 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í jako stejné, GetHashCode() musí metoda pro každý objekt vrátit stejnou hodnotu. Pokud se však dva objekty nerovnají jako stejné, GetHashCode() metody pro tyto dva objekty nemusí vracet různé hodnoty.
Metoda GetHashCode() objektu musí konzistentně vrátit stejný hash kód, pokud neexistuje žádná změna stavu objektu, který určuje návratovou hodnotu System.Object.Equals metody objektu . Mějte na paměti, že platí pouze pro aktuální spuštění aplikace a že je možné vrátit jiný kód hash, pokud je aplikace spuštěna znovu.
Pro zajištění nejlepšího výkonu by funkce hash měla vygenerovat rovnoměrnou distribuci pro všechny vstupy, včetně vstupu, který je silně clusterovaný. Implikací je, že malé úpravy stavu objektu by měly vést k velkým úpravám výsledného kódu hash pro nejlepší výkon hashovací tabulky.
Funkce hash by měly být pro výpočty levné.
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. Proto dva String objekty vrátí stejný hash kód, pokud představují stejnou řetězcovou hodnotu. Metoda také používá všechny znaky v řetězci ke generování přiměřeně náhodně distribuovaného výstupu, i když je vstup seskupený 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é hashové funkce 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, trvá hledání prvku konstantní čas (například operace O(1). V tabulce hash s špatnou implementací funkce hash závisí výkon vyhledává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 generují časté kolize.
Pokud velká část objektů v tabulce hash vytváří kódy hash, které jsou stejné nebo přibližně rovny sobě navzájem.
Když uživatelé zadávají data, ze kterých se vypočítá kód hash.
Odvozené třídy, které přepisují GetHashCode() , musí také přepsat Equals(Object) , aby zajistily, že dva objekty považovány za stejné mají stejný hash kód; jinak Hashtable typ nemusí fungovat správně.