Object.GetHashCode Metoda
Definicja
Ważne
Niektóre informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany przed wydaniem. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.
Służy jako domyślna funkcja skrótu.
public:
virtual int GetHashCode();
public virtual int GetHashCode();
abstract member GetHashCode : unit -> int
override this.GetHashCode : unit -> int
Public Overridable Function GetHashCode () As Integer
Zwraca
Kod skrótu dla bieżącego obiektu.
Przykłady
Jednym z najprostszych sposobów obliczania kodu skrótu dla wartości liczbowej, która ma taki sam lub mniejszy zakres niż Int32 typ, jest po prostu zwrócenie tej wartości. W poniższym przykładzie pokazano implementację 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
Często typ zawiera wiele pól danych, które mogą uczestniczyć w generowaniu kodu skrótu. Jednym ze sposobów wygenerowania kodu skrótu jest połączenie tych pól przy użyciu XOR (eXclusive OR) operacji, jak pokazano w poniższym przykładzie.
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
Poprzedni przykład zwraca ten sam kod skrótu dla (n1, n2) i (n2, n1), dlatego może wygenerować więcej kolizji niż są pożądane. Na platformie .NET 5+ zalecanym rozwiązaniem jest użycie HashCode.Combine. Pozwala uniknąć problemu z symetrią i tworzy dobrze rozproszony kod skrótu bez konieczności tworzenia Tuple obiektu.
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
Uwagi
Metoda GetHashCode udostępnia kod skrótu dla algorytmów wymagających szybkiego sprawdzania równości obiektów. Kod skrótu to wartość liczbowa używana do wstawiania i identyfikowania obiektu w kolekcji opartej na skrótach, takich jak Dictionary<TKey,TValue> klasa, Hashtable klasa lub typ pochodzący z DictionaryBase klasy.
Note
Aby uzyskać informacje na temat sposobu użycia kodów haszujących w tabelach haszujących oraz dodatkowych algorytmów kodu haszującego, zobacz wpis Funkcji haszującej w Wikipedii.
Dwa obiekty, które są równe, zwracają równe kody skrótów. Jednak odwrotnie nie jest prawdą: równe kody skrótów nie oznaczają równości obiektów, ponieważ różne (nierówne) obiekty mogą mieć identyczne kody skrótów. Ponadto .NET nie gwarantuje domyślnej implementacji metody GetHashCode, a wartość zwracana przez tę metodę może się różnić między implementacjami .NET i platformami, takimi jak między platformami 32-bitowymi i 64-bitowymi. Z tych powodów nie należy używać domyślnej implementacji tej metody jako unikatowego identyfikatora obiektu do celów tworzenia skrótów. Z tego wynikają dwie konsekwencje:
- Nie należy zakładać, że równe kody skrótów oznaczają równość obiektów.
- Nigdy nie należy utrwalać ani używać kodu skrótu poza domeną aplikacji, w której został utworzony, ponieważ ten sam obiekt może zawierać skrót między domenami aplikacji, procesami i platformami.
Warning
Kod skrótu jest przeznaczony do wydajnego wstawiania i wyszukiwania w kolekcjach opartych na tabeli skrótów. Kod skrótu nie jest wartością stałą. Z tego powodu:
- Nie serializuj wartości kodu skrótu ani nie przechowuj ich w bazach danych.
- Nie należy używać kodu skrótu jako klucza do pobierania obiektu z kolekcji indeksowanej kluczem.
- Nie wysyłaj kodów skrótów między domenami aplikacji ani procesami. W niektórych przypadkach kody skrótów mogą być obliczane dla poszczególnych procesów lub dla domeny aplikacji.
- Nie używaj kodu skrótu zamiast wartości zwracanej przez funkcję skrótów kryptograficznych, jeśli potrzebujesz kryptograficznie silnego skrótu. W przypadku skrótów kryptograficznych użyj klasy pochodnej po klasie System.Security.Cryptography.HashAlgorithm lub System.Security.Cryptography.KeyedHashAlgorithm.
- Nie sprawdzaj równości kodów skrótów, aby określić, czy dwa obiekty są równe. (Nierówne obiekty mogą mieć identyczne kody skrótów). Aby przetestować równość, wywołaj metodę ReferenceEquals or Equals .
Metodę GetHashCode można zastąpić typem pochodnym. Jeśli GetHashCode nie jest zastępowany, kody skrótów dla typów odwołań są obliczane przez wywołanie Object.GetHashCode metody klasy bazowej, która oblicza kod skrótu na podstawie odwołania do obiektu; aby uzyskać więcej informacji, zobacz RuntimeHelpers.GetHashCode. Innymi słowy, dwa obiekty, dla których ReferenceEquals metoda zwraca true , mają identyczne kody skrótów. Jeśli typy wartości nie zastępują GetHashCode, metoda ValueType.GetHashCode klasy bazowej używa odbicia do obliczenia kodu skrótu na podstawie wartości pól typu. Innymi słowy, typy wartości, których pola mają równe wartości, mają równe kody skrótów. Więcej informacji na temat zastępowania GetHashCode znajdziesz w sekcji "Uwagi do dziedziczących".
Warning
Jeśli zastąpisz metodę GetHashCode, należy również zastąpić metodę Equals, i odwrotnie. Jeśli przesłonięta metoda Equals zwraca wartość true podczas, gdy dwa obiekty są testowane pod kątem równości, przesłonięta metoda GetHashCode musi zwrócić tę samą wartość dla tych dwóch obiektów.
Jeśli obiekt, który jest używany jako klucz w tabeli skrótów, nie zapewnia użytecznej implementacji GetHashCode, można określić dostawcę kodu skrótu, dostarczając implementację IEqualityComparer do jednego z przeciążeń konstruktora klasy Hashtable.
Notatki dotyczące dziedziczenia
Funkcja skrótu służy do szybkiego generowania liczby (kodu skrótu), która odpowiada wartości obiektu. Funkcje skrótu są zwykle specyficzne dla każdego typu, a w przypadku unikatowości muszą używać co najmniej jednego z pól wystąpienia jako danych wejściowych. Kody skrótów nie powinny być obliczane przy użyciu wartości pól statycznych.
W przypadku klas pochodnych Objectz GetHashCode metody metoda może delegować do implementacji klasy GetHashCode() bazowej tylko wtedy, gdy klasa pochodna definiuje równość jako równość odwołania. Domyślna implementacja dla typów referencyjnych GetHashCode() zwraca kod skrótu, który jest odpowiednikiem tego, który jest zwracany przez metodę GetHashCode(Object) . Można zastąpić niezmienne GetHashCode() typy odwołań. Ogólnie rzecz biorąc, w przypadku typów odwołań modyfikowalnych należy zastąpić GetHashCode() tylko wtedy, gdy:
Możesz obliczyć kod skrótu z pól, które nie są modyfikowalne; Lub
Możesz upewnić się, że kod skrótu obiektu modyfikowalnego nie zmienia się, gdy obiekt znajduje się w kolekcji, która opiera się na jego kodzie skrótu.
W przeciwnym razie można pomyśleć, że obiekt modyfikowalny zostanie utracony w tabeli skrótów. Jeśli zdecydujesz się zastąpić GetHashCode() dla modyfikowalnego typu odwołania, dokumentacja powinna wyjaśnić, że użytkownicy typu nie powinni modyfikować wartości obiektów, gdy obiekt jest przechowywany w tabeli skrótów.
W przypadku typów GetHashCode() wartości udostępnia domyślną implementację kodu skrótu, która używa odbicia. Należy rozważyć zastąpienie go w celu uzyskania lepszej wydajności.
Aby uzyskać więcej informacji i przykłady dotyczące kodów skrótów obliczeniowych na różne sposoby, zobacz sekcję Przykłady.
Funkcja skrótu musi mieć następujące właściwości:
Jeśli dwa obiekty są porównywane jako równe, GetHashCode() metoda dla każdego obiektu musi zwrócić tę samą wartość. Jeśli jednak dwa obiekty nie są porównywane jako równe, GetHashCode() metody dla tych dwóch obiektów nie muszą zwracać różnych wartości.
GetHashCode() Metoda obiektu musi stale zwracać ten sam kod skrótu, o ile nie ma modyfikacji stanu obiektu, który określa wartość zwracaną metody System.Object.Equals obiektu. Należy pamiętać, że dotyczy to tylko bieżącego wykonywania aplikacji i że w przypadku ponownego uruchomienia aplikacji może zostać zwrócony inny kod skrótu.
Aby uzyskać najlepszą wydajność, funkcja skrótu powinna wygenerować równomierną dystrybucję dla wszystkich danych wejściowych, w tym danych wejściowych, które są silnie klastrowane. Implikacją jest to, że małe modyfikacje stanu obiektu powinny spowodować duże modyfikacje wynikowego kodu skrótu w celu uzyskania najlepszej wydajności tabeli skrótów.
Funkcje skrótu powinny być niedrogie do obliczeń.
Metoda GetHashCode() nie powinna zgłaszać wyjątków.
Na przykład implementacja metody dostarczonej GetHashCode() przez String klasę zwraca identyczne kody skrótów dla identycznych wartości ciągów. W związku z tym dwa String obiekty zwracają ten sam kod skrótu, jeśli reprezentują tę samą wartość ciągu. Ponadto metoda używa wszystkich znaków w ciągu do generowania rozsądnie losowo rozproszonych danych wyjściowych, nawet jeśli dane wejściowe są klastrowane w niektórych zakresach (na przykład wielu użytkowników może mieć ciągi zawierające tylko mniejsze 128 znaków ASCII, mimo że ciąg może zawierać dowolny z 65 535 znaków Unicode).
Zapewnienie dobrej funkcji skrótu w klasie może znacząco wpłynąć na wydajność dodawania tych obiektów do tabeli skrótów. W tabeli skrótów z kluczami, które zapewniają dobrą implementację funkcji skrótu, wyszukiwanie elementu wymaga stałego czasu (na przykład operacji O(1). W tabeli skrótów ze słabą implementacją funkcji skrótu wydajność wyszukiwania zależy od liczby elementów w tabeli skrótów (na przykład operacji O(n), gdzie n jest to liczba elementów w tabeli skrótów. Złośliwy użytkownik może wprowadzać dane wejściowe, które zwiększają liczbę kolizji, co może znacznie obniżyć wydajność aplikacji, które zależą od tabel skrótów, w następujących warunkach:
Gdy funkcje skrótu generują częste kolizje.
Gdy duża część obiektów w tabeli skrótów generuje kody skrótów, które są równe lub w przybliżeniu równe sobie.
Gdy użytkownicy wejdą dane, z których jest obliczany kod skrótu.
Klasy pochodne, które zastępują GetHashCode() , muszą również zastąpić Equals(Object) , aby zagwarantować, że dwa obiekty uważane za równe mają ten sam kod skrótu; w przeciwnym razie Hashtable typ może nie działać poprawnie.