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

Valori restituiti

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 inferiore rispetto al Int32 tipo consiste nel restituire 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 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

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

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

L'esempio precedente restituisce lo stesso codice hash per (n1, n2) e (n2, n1) e quindi potrebbe generare più conflitti di quanto siano auspicabili. In .NET 5+, la soluzione consigliata consiste nell'usare HashCode.Combine. Evita il problema di simmetria e produce un codice hash ben distribuito senza il sovraccarico di creazione di un Tuple oggetto.

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

Commenti

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

Note

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 (diversi) possono avere codici hash identici. Inoltre, .NET non garantisce l'implementazione predefinita del metodo GetHashCode e il valore restituito da questo metodo potrebbe differire tra implementazioni e piattaforme .NET, ad esempio tra piattaforme a 32 bit e a 64 bit. Per questi motivi, non usare l'implementazione predefinita di questo metodo come identificatore di oggetto univoco a scopo di hashing. Di seguito sono riportate due conseguenze:

  • Non si deve presupporre che i codici hash uguali implicano l'uguaglianza degli oggetti.
  • Non è mai consigliabile rendere persistente o usare un codice hash all'esterno del dominio applicazione in cui è stato creato, perché lo stesso oggetto può eseguire l'hashing tra domini, processi e piattaforme dell'applicazione.

Avvertimento

Un codice hash è destinato all'inserimento e alla ricerca efficienti nelle raccolte basate su una tabella hash. Un codice hash non è un valore permanente. Per questo motivo:

  • Non serializzare i valori del 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 applicazione o processi. In alcuni casi, i codici hash possono essere calcolati per ogni processo o per dominio applicazione.
  • Non usare il codice hash anziché un valore restituito da una funzione hash crittografica 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 verificare l'uguaglianza dei codici hash per determinare se due oggetti sono uguali. Gli oggetti diversi 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 GetHashCode non viene sottoposto a override, i codici hash per i tipi di riferimento vengono calcolati chiamando il Object.GetHashCode metodo della classe di base, che calcola un codice hash basato sul riferimento di un oggetto. Per altre informazioni, vedere RuntimeHelpers.GetHashCode. In altre parole, due oggetti per i quali il ReferenceEquals metodo restituisce true hanno codici hash identici. Se i tipi valore non eseguono l'override di GetHashCode, il metodo della classe base usa ValueType.GetHashCode reflection per calcolare il codice hash in base ai valori dei campi del tipo. In altre parole, i tipi valore i cui campi hanno valori uguali hanno codici hash uguali. Per altre informazioni sull'override di GetHashCode, vedere la sezione "Note per gli ereditari".

Avvertimento

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

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

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 l'univocità, devono usare almeno uno dei campi dell'istanza come input. I codici hash non devono essere calcolati usando i valori dei campi statici.

Per le classi derivate da Object, il GetHashCode metodo può delegare all'implementazione della classe GetHashCode() base solo se la classe derivata definisce l'uguaglianza di riferimento. L'implementazione predefinita di GetHashCode() per i tipi riferimento restituisce un codice hash equivalente a quello restituito dal GetHashCode(Object) metodo . È possibile eseguire l'override GetHashCode() 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 dai campi che non sono modificabili; O

  • È possibile assicurarsi che il codice hash di un oggetto modificabile non venga modificato 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 per un tipo di riferimento modificabile, la documentazione GetHashCode() deve rendere chiaro che gli utenti del tipo non devono modificare i valori degli oggetti mentre l'oggetto è archiviato in una tabella hash.

Per i tipi valore, GetHashCode() fornisce un'implementazione predefinita del codice hash che usa la reflection. È consigliabile valutarne 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 GetHashCode() metodi per i due oggetti non devono restituire valori diversi.

  • Il GetHashCode() 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 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 di nuovo.

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

  • Le funzioni hash devono essere poco costose per il calcolo.

  • Il GetHashCode() metodo non deve generare eccezioni.

Ad esempio, l'implementazione del GetHashCode() metodo fornito dalla String classe restituisce codici hash identici per valori stringa 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 un output distribuito ragionevolmente casuale, anche quando l'input viene raggruppato in determinati intervalli (ad esempio, molti utenti potrebbero avere stringhe che contengono solo i caratteri ASCII inferiori 128, anche se una stringa può contenere uno qualsiasi dei 65.535 caratteri Unicode).

Fornire una buona funzione hash in una classe può influire significativamente sulle prestazioni dell'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 scarsa di una funzione hash, le prestazioni di una ricerca dipendono dal numero di elementi nella tabella hash, ad esempio un'operazione O(n), dove n è il numero di elementi nella tabella hash. Un utente malintenzionato può inserire dati che aumentano il numero di conflitti, che possono ridurre significativamente le prestazioni delle applicazioni che dipendono dalle tabelle hash, nelle condizioni seguenti:

  • Quando le funzioni hash producono conflitti frequenti.

  • Quando una percentuale elevata di oggetti in una tabella hash produce codici hash uguali o approssimativamente uguali l'uno all'altro.

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

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

Si applica a

Vedi anche