Esporta (0) Stampa
Espandi tutto
Il presente articolo è stato tradotto automaticamente. Passare il puntatore sulle frasi nell'articolo per visualizzare il testo originale. Ulteriori informazioni.
Traduzione
Originale

Metodo Object.GetHashCode

Funge da funzione hash per un particolare tipo.

Spazio dei nomi:  System
Assembly:  mscorlib (in mscorlib.dll)

public virtual int GetHashCode()

Valore restituito

Tipo: System.Int32
Codice hash per l'oggetto corrente.

Un codice hash è un valore numerico utilizzato per inserire e identificare un oggetto a una in una raccolta basata su hash come classe Dictionary<TKey, TValue>, la classe Hashtable, o un tipo derivato dalla classe DictionaryBase. Può servire anche come indice per un oggetto all'interno di una collezione.

NotaNota

Per informazioni su come i codici hash vengono utilizzati nelle tabelle hash e per alcuni algoritmi aggiuntivi di codice hash, vedere la voce Funzione hash su Wikipedia.

L'implementazione predefinita del metodo GetHashCode non garantisce che vengano restituiti valori univoci quando è invocato su oggetti differenti. Tuttavia, non vale il contrario: codici hash uguali non implicano l'uguaglianza degli oggetti, poiché oggetti diversi (non uguali) possono avere codici hash uguali. Di conseguenza, l'implementazione predefinita di questo metodo non deve essere utilizzata come un identificatore univoco per scopi di hashing. Il metodo GetHashCode può essere ridefinito da un tipo derivato. I tipi valore devono ridefinire questo metodo per fornire una funzione hash appropriata per quel tipo e per garantire una distribuzione adeguata per l'utilizzo in una tabella hash.

  • Per garantire l'univocità, il codice hash deve essere basato sul valore di un campo o di una proprietà dell'istanza piuttosto che su un campo o una proprietà statica.

  • Gli oggetti utilizzati come chiave in un oggetto Hashtable devono inoltre ridefinire il metodo GetHashCode, perché tali oggetti devono generare il proprio codice hash.

Nota di avvisoAttenzione

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

  • Non devono essere serializzati i valori di codice hash e non devono essere memorizzati nei database.

  • Non deve essere utilizzato il codice hash come chiave per recuperare un oggetto da una raccolta con chiavi.

  • Non devono essere inviare i codici hash tra i domini di applicazioni o i processi. In alcuni casi, i codici hash possono essere calcolati alla base di un processo o un dominio di applicazione.

  • Non deve essere utilizzato il codice hash al posto di un valore restituito da una funzione di crittografia di hashing se è necessaria una crittografia robusta basata su hash. Per gli hash di crittografia, utilizzare la classe derivata dalla classe System.Security.Cryptography.KeyedHashAlgorithm o System.Security.Cryptography.HashAlgorithm.

  • Non si verifica l'uguaglianza dei codici hash per determinare se due oggetti sono uguali. (Oggetti diversi possono avere codici hash identici.) Per verificare l'uguaglianza, chiamare il metodo Equals o ReferenceEquals.

Una funzione di hash deve avere le seguenti proprietà: Se su GetHashCode non viene eseguito l'override, i codici hash per i tipi di riferimento vengono calcolati chiamando il metodo Object.GetHashCode della classe base, che calcola un codice hash basato sul riferimento di un oggetto; per ulteriori informazioni, vedere RuntimeHelpers.GetHashCode. In altre parole, due oggetti per i quali il metodo true ritorna ReferenceEquals hanno codici hash uguali. Se i tipi di valore non eseguono l'override di GetHashCode, il metodo ValueType.GetHashCode della classe base utilizza reflection per calcolare il codice hash basato sui valori dei campi del tipo. Ovvero, i tipi di valori, i cui campi hanno valori uguali, hanno codici hash uguali. Per ulteriori informazioni sull'override GetHashCode, vedere la sezione "note per gli eredi".

Nota di avvisoAttenzione

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

Se un oggetto è utilizzato come una 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 di classe Hashtable.

Note per il Windows Runtime

Quando si chiama l'overload del metodo GetHashCode su una classe nel Windows Runtime, fornisce il comportamento predefinito per le classi che non eseguono l'override GetHashCode. Ciò fa parte del supporto fornito da .NET Framework per Windows Runtime (vedere Supporto .NET Framework per applicazioni Windows Store e Windows Runtime. Le classi nel Windows Runtime non ereditano Object e attualmente non implementano un GetHashCode. Tuttavia, sembrano avere i metodi ToString, Equals(Object) e GetHashCode quando vengono utilizzati nel codice C# o Visual Basic, e .NET Framework fornisce il comportamento predefinito per tali metodi.

NotaNota

Le funzioni hash non devono essere dispendiose da un punto di vista computazionale.

Note per gli eredi

Le implementazioni del metodo GetHashCode non devono produrre come risultato dei riferimenti circolari. Le funzioni hash sono in genere specifiche per ciascun tipo e, per univocità, devono utilizzare come input almeno uno dei campi dell'istanza. I codici hash non devono essere calcolati utilizzando i valori dei campi statici.

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

  • È possibile calcolare il codice hash dei campi che non sono modificabili; o

  • È possibile assicurarsi che il codice hash di un oggetto modificabile non cambi quando l'oggetto è contenuto in una raccolta basata sul suo codice hash.

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

Per i tipi di valore, ValueType.GetHashCode fornisce un'implementazione di codice hash predefinita che utilizza reflection. È consigliabile eseguire l'override del metodo per ottenere prestazioni migliori.

NotaNota

Per ulteriori informazioni ed esempi che calcolano codici hash in diversi modi, vedere la sezione relativa agli esempi.

Una funzione hash deve presentare le seguenti proprietà:

  • Se due oggetti confrontati risultano uguali, il metodo GetHashCode per ogni oggetto dovrà restituire lo stesso valore. Tuttavia, se due oggetti confrontati non risultano uguali, i metodi GetHashCode per i due oggetti non dovranno restituire valori diversi.

  • Il metodo GetHashCode per un oggetto deve restituire coerentemente lo stesso codice hash finché lo stato dell'oggetto non viene modificato in modo da determinare il valore restituito del metodo Equals dell'oggetto. Si noti che ciò vale solo per l'esecuzione corrente di un'applicazione e che può essere restituito un codice hash diverso se l'applicazione viene eseguita nuovamente.

  • Per prestazioni ottimali, una funzione hash deve generare una distribuzione uguale per tutti gli input, inclusi gli input pesantemente clusterizzati. Un'implicazione è che piccole modifiche allo stato dell'oggetto comportano grandi modifiche al codice hash risultante per migliori prestazioni per la tabella hash.

  • L'elaborazione di funzioni hash non dovrebbe essere pesante.

  • Il metodo GetHashCode non dovrebbe generare eccezioni.

L'implementazione del metodo GetHashCode fornita dalla classe String, ad esempio, restituisce codici hash identici per valori stringa identici. Di conseguenza, due oggetti String restituiscono lo stesso codice hash se rappresentano lo stesso valore stringa. Il metodo utilizza inoltre nella stringa tutti i caratteri per generare un output ragionevolmente distribuito in modo casuale, anche quando l'input è suddiviso in cluster in alcuni intervalli. Molti utenti possono, ad esempio, avere stringhe contenenti soltanto i 128 caratteri ASCII inferiori, anche se una stringa può contenere qualsiasi dei 65.535 caratteri Unicode.

Fornire una buona funzione hash su una classe può influire in modo significativo sull'aggiunta di tali oggetti a una tabella hash. In una tabella hash con chiavi che non fornisce una buona implementazione di una funzione hash, la ricerca di un elemento può richiedere tempo costante (ad esempio O(1)). In una tabella hash con una scarsa implementazione di una funzione hash, le prestazioni di una ricerca dipendono dal numero di elementi presenti nella tabella hash, ad esempio, un'operazione O( n ), dove n è il numero di voci presenti nella tabella hash. Un malintenzionato può inserire dati che aumentano il numero di conflitti, che possono ridurre significativamente le prestazioni delle applicazioni che dipendono da tabelle hash, sotto le seguenti condizioni:

  • Quando le funzioni hash producono frequenti conflitti.

  • Quando una notevole percentuale di oggetti in un hash tabella codici hash dei prodotti che sono uguali o quasi uguale all'altro.

  • Quando gli utenti inseriscono i dati dai quali viene calcolato il codice hash.

Le classi derivate che eseguono l'override del metodo GetHashCode devono eseguire anche l'override del metodo Equals per garantire che due oggetti considerati uguali abbiano lo stesso codice hash; in caso contrario, il tipo Hashtable potrebbe non funzionare correttamente.

Uno dei modi più semplici per calcolare il codice hash per un valore numerico con lo stesso o un minore intervallo del tipo Int32 è semplicemente quello di restituire quel valore. Nell'esempio riportato di seguito viene mostrata un'implementazione per una struttura 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 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


Un tipo dispone spesso di più campi dati in grado di prendere parte alla creazione del codice hash. Un modo per generare un codice hash consiste nel combinare questi campi utilizzando un'operazione XOR (eXclusive OR), come indicato nell'esempio che segue.


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


L'esempio precedente restituisce lo stesso codice hash per (n1, n2) e (n2, n1), pertanto può generare più conflitti di quanto ci si aspetta. Un certo numero di soluzioni sono disponibili in modo che i codici hash in questi casi non siano identici. Uno è di restituire il codice hash di un oggetto Tuple che riflette l'ordine di ogni campo. Nell'esempio riportato di seguito viene illustrata una possibile implementazione che fa uso della classe Tuple<T1, T2>. Si noti, tuttavia, che il carico di lavoro nel creare un'istanza di un oggetto Tuple può ridurre significativamente le prestazioni di un'applicazione che contiene un numero elevato di oggetti 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)) return false;

       Point p = (Point) obj;
       return x == p.x & y == p.y;
    }

    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


Una seconda soluzione alternativa include la valutazione dei singoli codici hash eseguendo lo shift a sinistra dei codici hash dei campi successivi di due o più bit. In maniera ottimale, anziché essere scartati, i bit spostati oltre il bit 31 vengono racchiusi. Poiché i bit vengono rimossi da operatori di shift a sinistra in entrambi i linguaggi C# e Visual Basic, è necessario creare un metodo di shift a sinistra e wrap come segue:


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);
}


Nell'esempio viene quindi utilizzato il metodo di shift e wrap per calcolare il codice hash della struttura Point utilizzata 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 


.NET Framework

Supportato in: 4.5.2, 4.5.1, 4.5, 4, 3.5, 3.0, 2.0, 1.1, 1.0

.NET Framework Client Profile

Supportato in: 4, 3.5 SP1

Libreria di classi portabile

Supportato in: Libreria di classi portabile

.NET per applicazioni Windows Store

Supportato in: Windows 8

.NET per applicazioni Windows Phone

Supportato in: Windows Phone 8, Silverlight 8.1

Windows Phone 8.1, Windows Phone 8, Windows 8.1, Windows Server 2012 R2, Windows 8, Windows Server 2012, Windows 7, Windows Vista SP2, Windows Server 2008 (ruoli di base del server non supportati), Windows Server 2008 R2 (ruoli di base del server supportati con SP1 o versione successiva, Itanium non supportato)

.NET Framework non supporta tutte le versioni di ciascuna piattaforma. Per un elenco delle versioni supportate, vedere Requisiti di sistema di .NET Framework.

Aggiunte alla community

AGGIUNGI
Mostra:
© 2015 Microsoft