Object.GetHashCode Metodo

Definizione

Funge da funzione hash predefinita.

public:
 virtual int GetHashCode();
public virtual int GetHashCode ();
abstract member GetHashCode : unit -> int
override this.GetHashCode : unit -> int
Public Overridable Function GetHashCode () As Integer

Restituisce

Int32

Codice hash per l'oggetto corrente.

Esempio

Uno dei modi più semplici per calcolare un codice hash per un valore numerico con lo stesso intervallo o un intervallo più piccolo del tipo è restituire Int32 semplicemente tale valore. Nell'esempio seguente viene illustrata un'implementazione di questo tipo per una Number struttura .

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

Un tipo ha spesso più campi dati che possono partecipare alla generazione del codice hash. Un modo per generare un codice hash è combinare questi campi usando XOR (eXclusive OR) un'operazione, come illustrato nell'esempio seguente.

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

L'esempio precedente restituisce lo stesso codice hash per (n1, n2) e (n2, n1) e pertanto può generare più collisioni di quanto sia auspicabile. Sono disponibili diverse soluzioni in modo che i codici hash in questi casi non siano identici. Uno è restituire il codice hash di un Tuple oggetto che riflette l'ordine di ogni campo. Nell'esempio seguente viene illustrata una possibile implementazione che usa la Tuple<T1,T2> classe . Si noti tuttavia che l'overhead delle prestazioni della creazione di un'istanza di un oggetto può influire in modo significativo sulle prestazioni complessive di un'applicazione che archivia un numero elevato di oggetti Tuple nelle tabelle 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
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

Una seconda soluzione alternativa prevede la ponderazione dei singoli codici hash spostando a sinistra i codici hash dei campi successivi di due o più bit. In modo ottimale, i bit spostati oltre il bit 31 devono essere incapsulati anziché essere eliminati. Poiché i bit vengono eliminati dagli operatori di spostamento a sinistra in C# e Visual Basic, è necessario creare un metodo di spostamento a sinistra e ritorno a capo come il seguente:

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

Nell'esempio seguente viene quindi usato questo metodo shift-and-wrap per calcolare il codice hash della Point struttura usata negli esempi precedenti.

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

Commenti

Un codice hash è un valore numerico usato per inserire e identificare un oggetto in una raccolta basata su hash, ad esempio la classe , la classe o un tipo Dictionary<TKey,TValue> Hashtable derivato dalla DictionaryBase classe . Il GetHashCode metodo fornisce questo codice hash per gli algoritmi che necessitano di controlli rapidi dell'uguaglianza degli oggetti.

Nota

Per informazioni sull'uso dei codici hash nelle tabelle hash e per alcuni algoritmi di codice hash aggiuntivi, vedere la voce Funzione hash in Wikipedia.

Due oggetti uguali restituiscono codici hash uguali. Tuttavia, il contrario non è vero: i codici hash uguali non implicano l'uguaglianza degli oggetti, perché oggetti diversi (non uguali) possono avere codici hash identici. Inoltre, .NET non garantisce l'implementazione predefinita del metodo e il valore restituito da questo metodo può differire tra le implementazioni .NET, ad esempio versioni diverse di .NET Framework e .NET Core, e piattaforme, ad esempio piattaforme GetHashCode a 32 bit e a 64 bit. Per questi motivi, non usare l'implementazione predefinita di questo metodo come identificatore di oggetto univoco ai fini dell'hashing. Di seguito sono seguite due conseguenze:

  • Non è consigliabile presupporre che i codici hash uguali implicano l'uguaglianza degli oggetti.

  • Non è mai consigliabile rendere persistente o usare un codice hash esterno al dominio dell'applicazione in cui è stato creato, perché lo stesso oggetto può eseguire l'hash tra domini applicazione, processi e piattaforme.

Avviso

Un codice hash è progettato per l'inserimento e la ricerca efficienti nelle raccolte basate su una tabella hash. Un codice hash non è un valore permanente. Per questo motivo:

  • Non serializzare i valori di codice hash o archiviarli nei database.
  • Non usare il codice hash come chiave per recuperare un oggetto da una raccolta con chiave.
  • Non inviare codici hash tra domini o processi dell'applicazione. In alcuni casi, i codici hash possono essere calcolati in base al processo o al dominio dell'applicazione.
  • Non usare il codice hash anziché un valore restituito da una funzione di hashing crittografico se è necessario un hash crittografico sicuro. Per gli hash crittografici, usare una classe derivata dalla System.Security.Cryptography.HashAlgorithm classe o System.Security.Cryptography.KeyedHashAlgorithm .
  • Non testare l'uguaglianza dei codici hash per determinare se due oggetti sono uguali. Gli oggetti non uguali possono avere codici hash identici. Per verificare l'uguaglianza, chiamare il ReferenceEquals metodo o Equals .

Il GetHashCode metodo può essere sottoposto a override da un tipo derivato. Se non viene eseguito l'override, i codici hash per i tipi riferimento vengono calcolati chiamando il metodo della classe di base, che calcola un codice hash in base al riferimento di un oggetto. Per altre informazioni, vedere GetHashCode Object.GetHashCode RuntimeHelpers.GetHashCode . In altre parole, due oggetti per cui il ReferenceEquals metodo restituisce hanno codici hash true identici. Se i tipi valore non eseguono l'override di , il metodo della classe base usa la reflection per calcolare il codice hash in base ai valori dei GetHashCode ValueType.GetHashCode campi del tipo. In altre parole, i tipi valore i cui campi hanno valori uguali hanno codici hash uguali. Per altre informazioni sull'override GetHashCode di , vedere la sezione "Note per gli eredi".

Avviso

Se si esegue l'override GetHashCode del metodo , è necessario eseguire anche l'override di e Equals viceversa. Se il metodo sottoposto a override restituisce quando viene verificata l'uguaglianza di due oggetti, il metodo sottoposto a override deve restituire lo stesso Equals valore per i due true GetHashCode oggetti.

Se un oggetto utilizzato come chiave in una tabella hash non fornisce un'implementazione utile di , è possibile specificare un provider di codice hash fornendo un'implementazione a uno degli overload del costruttore della GetHashCode IEqualityComparer Hashtable classe.

Note per il runtime di Windows

Quando si chiama il metodo su una classe in Windows Runtime, fornisce il comportamento predefinito per le classi che non eseguono GetHashCode l'override di GetHashCode . Questo fa parte del supporto fornito dall'.NET Framework per Windows Runtime (vedere .NET Framework Support for Windows Store Apps and Windows Runtime). Le classi nel Windows Runtime non ereditano e attualmente Object non implementano GetHashCode . Tuttavia, sembrano avere metodi , e quando vengono utilizzati nel codice C# o Visual Basic e il .NET Framework fornisce il comportamento predefinito per ToString Equals(Object) questi GetHashCode metodi.

Nota

Windows Le classi di runtime scritte in C# o Visual Basic possono eseguire l'override del GetHashCode metodo .

Note per gli eredi

Una funzione hash viene usata per generare rapidamente un numero (codice hash) che corrisponde al valore di un oggetto . Le funzioni hash sono in genere specifiche di ogni tipo e, per univocità, devono usare almeno uno dei campi di istanza come input. I codici hash non devono essere calcolati usando i valori dei campi statici.

Per le classi derivate da , il metodo può delegare all'implementazione della classe di base solo se la classe derivata definisce l'uguaglianza come uguaglianza Object GetHashCode dei GetHashCode() riferimenti. L'implementazione predefinita di per i tipi riferimento restituisce un codice hash equivalente a quello GetHashCode() restituito dal GetHashCode(Object) metodo . È possibile eseguire GetHashCode() l'override per i tipi riferimento non modificabili. In generale, per i tipi riferimento modificabili, è consigliabile eseguire l'override GetHashCode() solo se:

  • È possibile calcolare il codice hash da campi non modificabili. O
  • È possibile assicurarsi che il codice hash di un oggetto modificabile non cambi mentre l'oggetto è contenuto in una raccolta che si basa sul relativo codice hash.

In caso contrario, si potrebbe pensare che l'oggetto modificabile venga perso nella tabella hash. Se si sceglie di eseguire l'override di per un tipo di riferimento modificabile, la documentazione deve chiarire che gli utenti del tipo non devono modificare i valori dell'oggetto mentre l'oggetto è archiviato in una GetHashCode() tabella hash.

Per i tipi valore, GetHashCode() fornisce un'implementazione di codice hash predefinita che usa la reflection. È consigliabile eseguire l'override per ottenere prestazioni migliori.

Per altre informazioni ed esempi che calcolano i codici hash in diversi modi, vedere la sezione Esempi.

Una funzione hash deve avere le proprietà seguenti:

  • Se due oggetti vengono confrontati come uguali, il GetHashCode() metodo per ogni oggetto deve restituire lo stesso valore. Tuttavia, se due oggetti non vengono confrontati come uguali, i metodi per i due oggetti non devono GetHashCode() restituire valori diversi.

  • Il metodo per un oggetto deve restituire in modo coerente lo stesso codice hash purché non vi siano modifiche allo stato dell'oggetto che determina il valore restituito del metodo GetHashCode() System.Object.Equals dell'oggetto. Si noti che questo vale solo per l'esecuzione corrente di un'applicazione e che è possibile restituire un codice hash diverso se l'applicazione viene eseguita nuovamente.

  • Per ottenere prestazioni ottimali, una funzione hash deve generare una distribuzione uniforme per tutti gli input, incluso l'input che è molto cluster. Un'implicazione è che piccole modifiche allo stato dell'oggetto dovrebbero comportare modifiche di grandi dimensioni al codice hash risultante per ottenere prestazioni ottimali della tabella hash.

  • Le funzioni hash devono essere poco costose da calcolare.

  • Il GetHashCode() metodo non deve generare eccezioni.

Ad esempio, l'implementazione GetHashCode() del metodo fornito dalla classe restituisce codici hash identici per valori stringa String identici. Pertanto, due String oggetti restituiscono lo stesso codice hash se rappresentano lo stesso valore stringa. Inoltre, il metodo usa tutti i caratteri nella stringa per generare output ragionevolmente distribuito in modo casuale, anche quando l'input è raggruppato in determinati intervalli (ad esempio, molti utenti potrebbero avere stringhe che contengono solo i 128 caratteri ASCII inferiori, anche se una stringa può contenere uno dei 65.535 caratteri Unicode).

La fornitura di una buona funzione hash in una classe può influire in modo significativo sulle prestazioni di aggiunta di tali oggetti a una tabella hash. In una tabella hash con chiavi che forniscono una buona implementazione di una funzione hash, la ricerca di un elemento richiede tempo costante (ad esempio, un'operazione O(1). In una tabella hash con un'implementazione non corretta di una funzione hash, le prestazioni di una ricerca dipendono dal numero di elementi nella tabella hash (ad esempio, un'operazione O( ), dove è il numero di elementi nella n n tabella hash. Un utente malintenzionato può immettere dati che aumentano il numero di collisioni, che possono ridurre significativamente le prestazioni delle applicazioni che dipendono da tabelle hash, nelle condizioni seguenti:

  • Quando le funzioni hash producono collisioni frequenti.

  • Quando una percentuale elevata di oggetti in una tabella hash produce codici hash uguali o approssimativamente uguali tra loro.

  • Quando gli utenti immettere i dati da cui viene calcolato il codice hash.

Le classi derivate che eseguono l'override di devono inoltre eseguire l'override di per garantire che due oggetti considerati uguali hanno lo stesso codice hash. In caso contrario, il tipo GetHashCode() Equals(Object) potrebbe non funzionare Hashtable correttamente.

Si applica a

Vedi anche