Object.GetHashCode Metoda

Definice

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

Int32

Kód hash pro aktuální objekt.

Příklady

Jedním z nejjednodušších způsobů, jak vypočítat hash kód 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 Number struktury.

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
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 typ obsahuje více datových polí, které mohou být součástí generování kódu hash. Jedním ze způsobů, jak generovat 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.
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ětší kolizí, než je žádoucí. K dispozici je několik řešení, aby kódy hash v těchto případech nebyly identické. Jedním z nich je vrácení kódu hash Tuple objektu, který odráží pořadí jednotlivých polí. Následující příklad ukazuje možnou implementaci, která používá Tuple<T1,T2> třídu. Všimněte si ale, že nároky na výkon při vytváření instancí Tuple objektu mohou významně ovlivnit celkový výkon aplikace, která ukládá velký počet objektů v zatřiďovacích tabulkách.

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
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é alternativní řešení zahrnuje vážení jednotlivých kódů hash na základě levého posunu kódů hash po sobě jdoucích polí o dvě nebo více bitů. V optimálním případě by služba BITS byla přesunuta mimo bit 31, takže by se místo toho měla zalomit. vzhledem k tomu, že bity jsou zahozeny pomocí operátorů levého posunutí v C# i Visual Basic, je nutné vytvořit levou metodu shift a wrap, 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);
}
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 poté používá tuto metodu Shift-and-Wrap k výpočtu hash kódu Point struktury používané 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
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á se používá k vložení a identifikaci objektu v kolekci založené na hodnotě hash, jako je Dictionary<TKey,TValue> třída, Hashtable třída nebo typ odvozený z DictionaryBase třídy. GetHashCodeMetoda poskytuje tento kód hash pro algoritmy, které vyžadují rychlou kontrolu rovnosti objektů.

Poznámka

Informace o tom, jak se kódy hash používají v zatřiďovacích tabulkách a pro některé další algoritmy hash kódu, naleznete v tématu funkce hash v Wikipedii.

Dva objekty, které jsou stejné jako návratové kódy hash, které jsou stejné. Ale opak není true: stejné hodnoty hash neznamenají rovnost objektů, protože různé (nerovné) objekty můžou mít stejné kódy hash. rozhraní .net navíc nezaručuje výchozí implementaci GetHashCode metody a hodnota, kterou vrátí tato metoda, se může lišit mezi implementacemi rozhraní .net, například různými verzemi .NET Framework a .net Core a platformami, jako jsou 32 a 64-bitové platformy. Z těchto důvodů nepoužívejte výchozí implementaci této metody jako jedinečný identifikátor objektu pro účely výpočtu hodnoty hash. Existují dvě důsledky z těchto kroků:

  • Neměli byste předpokládat, že stejné hodnoty hash implikují rovnost objektů.

  • Nikdy nesmíte zachovat ani použít kód hash mimo doménu aplikace, ve které byl vytvořen, protože stejný objekt může mít hodnotu hash napříč doménami aplikace, 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 zatřiďovací tabulce. Kód hash není trvalá hodnota. Z tohoto důvodu:

  • Neserializovat hodnoty hash kódu ani ukládat je do databází.
  • Nepoužívejte kód hash jako klíč pro načtení objektu z kolekce s klíčem.
  • Neodesílají kódy hash napříč aplikačními doménami nebo procesy. V některých případech mohou být kódy hash vypočítány pro jednotlivé procesy nebo jednotlivé aplikační domény.
  • Nepoužívejte kód hash namísto hodnoty vrácené kryptografickou funkcí hash, pokud potřebujete kryptograficky silný algoritmus hash. Pro kryptografické hodnoty hash použijte třídu odvozenou z System.Security.Cryptography.HashAlgorithm třídy nebo System.Security.Cryptography.KeyedHashAlgorithm .
  • Neprovádějte test rovnosti kódů hash k určení, zda jsou dva objekty stejné. (Nerovné objekty můžou mít stejné kódy hash.) Chcete-li otestovat rovnost, zavolejte ReferenceEquals Equals metodu nebo.

GetHashCodeMetodu lze přepsat odvozeným typem. Pokud GetHashCode není přepsán, kódy hash pro typy odkazů jsou vypočítány voláním Object.GetHashCode metody základní třídy, která vypočítá kód hash založený na referenci objektu; Další informace naleznete v tématu RuntimeHelpers.GetHashCode . Jinými slovy, dva objekty, pro které bude ReferenceEquals Metoda vracet, true mají stejné kódy hash. Pokud typy hodnot nepřepisují 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é kódy hash. Další informace o přepsání GetHashCode najdete v části "poznámky k dědicům".

Upozornění

Pokud přepíšete GetHashCode metodu, měli byste také přepsat Equals a naopak. Pokud přepsaná Equals Metoda vrátí true , když jsou testovány dva objekty pro rovnost, přepsaná GetHashCode Metoda musí vracet stejnou hodnotu pro dva objekty.

Pokud objekt, který je použit jako klíč v zatřiďovací tabulce, neposkytuje užitečnou implementaci GetHashCode , můžete zadat poskytovatele kódu hash poskytnutím IEqualityComparer implementace do jednoho z přetížení Hashtable konstruktoru třídy.

poznámky k prostředí Windows Runtime

při volání GetHashCode metody pro třídu v prostředí Windows Runtime poskytuje výchozí chování pro třídy, které nepřepisujete GetHashCode . toto je součást podpory, kterou .NET Framework poskytuje pro prostředí Windows Runtime (viz podpora .NET Framework pro aplikace Windows store a prostředí Windows Runtime). třídy v prostředí Windows Runtime nedědí Object a aktuálně neimplementují GetHashCode . zdá se však, že existují ToString Equals(Object) metody, a, pokud je GetHashCode používáte 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, které jsou napsány v jazyce C# nebo Visual Basic mohou přepsat GetHashCode metodu.

Poznámky pro dědice

Funkce hash slouží k rychlému vygenerování čísla (kódu hash), který odpovídá hodnotě objektu. Funkce hash jsou obvykle specifické pro každý typ a v případě jedinečnosti musí použít alespoň jedno pole instance jako vstup. Kódy hash by se neměly vypočítávají pomocí hodnot statických polí.

Pro třídy, které jsou odvozeny z Object , GetHashCode metoda může delegovat na implementaci základní třídy pouze v případě, že GetHashCode() odvozená třída definuje rovnost, která bude odkazovat na rovnost. Výchozí implementace GetHashCode() pro typ odkazu vrátí hodnotu hash, která je ekvivalentní hodnotě, kterou vrátí GetHashCode(Object) metoda. Můžete přepsat GetHashCode() pro neměnné typy odkazů. Obecně platí, že pro mutable odkazové typy byste měli přepsat GetHashCode() pouze v případě, že: – Kód hash můžete vypočítat z polí, která nejsou změnitelná. Nebo – Můžete zajistit, aby se kód hash měnitelného objektu v době, kdy je objekt obsažený v kolekci, která spoléhá na svůj kód hash.

V opačném případě si můžete myslet, že se v tabulce hash ztratí muovatelný objekt. Pokud se rozhodnete přepsat pro měnitelný odkazový typ, v dokumentaci by mělo být zřejmé, že uživatelé vašeho typu by neměli upravovat hodnoty objektů, když je objekt uložený v GetHashCode() tabulce hash.

Pro typy hodnot poskytuje GetHashCode() výchozí implementaci kódu hash, která používá reflexi. Měli byste zvážit jeho přepsání kvůli lepšímu výkonu.

Další informace a příklady, které počítají hashovací kódy různými způsoby, najdete v části Příklady.

Hashovací funkce musí mít následující vlastnosti: – Pokud se dva objekty porovnávají jako stejné, musí metoda GetHashCode() pro každý objekt vrátit stejnou hodnotu. Pokud se ale dva objekty nerovnají, nemusí metody pro tyto dva objekty GetHashCode() vracet různé hodnoty.

– Metoda objektu musí konzistentně vracet stejný kód hash, pokud neexistuje žádná úprava stavu objektu, který určuje návratovou hodnotu metody GetHashCode() System.Object.Equals objektu. Všimněte si, že to platí pouze pro aktuální spuštění aplikace a že pokud se aplikace znovu spustí, může se vrátit jiný kód hash.

– Pro nejlepší výkon by měla hashovací funkce vygenerovat rovnoměrně rozdělení pro všechny vstupy, včetně vstupu, který je silně seskupený. Důsledkem 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 tabulky hash.

– Výpočetní funkce hash by měly být cenově dostupné.

– Metoda GetHashCode() by neměla vyvolat výjimky.

Například implementace metody poskytované třídou vrací identické hashovací kódy GetHashCode() String pro identické řetězcové hodnoty. Proto dva String objekty 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 seskupený v určitých rozsazích (například mnoho uživatelů může mít řetězce, které obsahují pouze méně než 128 znaků ASCII, i když řetězec může obsahovat libovolný z 65 535 znaků Unicode).

Poskytnutí vhodné hashovací funkce pro třídu může výrazně ovlivnit výkon přidávání těchto objektů do tabulky hash. V zatřiďovací tabulce s klíči, které poskytují dobrou implementaci funkce hash, trvá hledání prvku konstantní čas (například operace O(1). V zatřiďovací tabulce s špatnou implementací funkce hash závisí výkon vyhledávání na počtu položek v tabulce hash (například operace O( ), kde je počet položek v tabulce n n hash). Uživatel se zlými úmysly může zadat data, která zvýší 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ž hashovací funkce vytvářejí časté kolize.

– Pokud velký podíl objektů v zatřiďovací tabulce vytváří hashovací kódy, které se navzájem rovnají nebo se přibližně rovnají.

– Když uživatelé zadá data, ze kterých se počítá hashovací kód.

Odvozené třídy, které přepisují, musí také přepsat, aby bylo zaručeno, že dva objekty považované za stejné mají stejný kód hash. V opačném případě nemusí typ GetHashCode() Equals(Object) fungovat Hashtable správně.

Platí pro

Viz také