Object.GetHashCode Metoda

Definicja

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.

Dotyczy

Zobacz też