Object.GetHashCode Metodo
Definizione
Importante
Alcune informazioni sono relative alla release non definitiva del prodotto, che potrebbe subire modifiche significative prima della release definitiva. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.
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
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 semplicemente nel restituire 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
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 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
Spesso, un tipo include 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 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.
[<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 $"{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 quindi può generare più collisioni rispetto a quelle desiderate. Sono disponibili numerose soluzioni in modo che i codici hash in questi casi non siano identici. Uno consiste nel 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 il sovraccarico delle prestazioni di creazione di un'istanza di un oggetto può influire significativamente sulle prestazioni complessive di un'applicazione che archivia un Tuple
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)
{
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
[<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).GetHashCode()
let pt = Point(5, 8)
printfn $"{pt.GetHashCode()}"
let pt2 = Point(8, 5)
printfn $"{pt2.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 comporta il peso dei singoli codici hash spostando a sinistra i codici hash dei campi successivi in due o più bit. In modo ottimale, i bit spostati oltre il bit 31 devono essere incapsulati invece di essere eliminati. Poiché i bit vengono eliminati dagli operatori a sinistra sia in C# che in Visual Basic, è necessario creare un metodo shift-and-wrapping a sinistra 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);
}
let shiftAndWrap (value: int) positions =
let positions = positions &&& 0x1F
// Save the existing bit pattern, but interpret it as an unsigned integer.
let number = BitConverter.ToUInt32(BitConverter.GetBytes value, 0)
// Preserve the bits to be discarded.
let wrapped = number >>> (32 - positions)
// Shift and wrap the discarded bits.
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 maiusc-and-wrapping 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
open System
[<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 this.GetHashCode() =
this.ShiftAndWrap(x.GetHashCode(), 2) ^^^ y.GetHashCode()
member _.ShiftAndWrap(value, positions) =
let positions = positions &&& 0x1F
// Save the existing bit pattern, but interpret it as an unsigned integer.
let number = BitConverter.ToUInt32(BitConverter.GetBytes value, 0)
// Preserve the bits to be discarded.
let wrapped = number >>> (32 - positions)
// Shift and wrap the discarded bits.
BitConverter.ToInt32(BitConverter.GetBytes((number <<< positions) ||| wrapped), 0)
let pt = Point(5, 8)
printfn $"{pt.GetHashCode()}"
let pt2 = Point(8, 5)
printfn $"{pt2.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 utilizzato per inserire e identificare un oggetto in una raccolta basata su hash, ad esempio la classe, la Dictionary<TKey,TValue> Hashtable classe o un tipo derivato dalla DictionaryBase classe. Il GetHashCode metodo fornisce questo codice hash per gli algoritmi che richiedono controlli rapidi dell'uguaglianza degli oggetti.
Nota
Per informazioni sul modo in cui i codici hash vengono usati nelle tabelle hash e per alcuni algoritmi di codice hash aggiuntivi, vedere la voce Funzione hash in Wikipedia.
Due oggetti uguali a codici hash restituiti uguali. Tuttavia, il contrario non è vero: i codici hash uguali non implicano l'uguaglianza degli oggetti, perché gli 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ò variare tra implementazioni .NET, ad esempio versioni diverse di GetHashCode .NET Framework e .NET Core e piattaforme, ad esempio piattaforme 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. Due conseguenze seguono questo:
Non è consigliabile presupporre che i codici hash uguali implicano l'uguaglianza degli oggetti.
Non è mai necessario mantenere o usare un codice hash all'esterno del dominio dell'applicazione in cui è stato creato, perché lo stesso oggetto può eseguire l'hash tra domini, processi e piattaforme dell'applicazione.
Avviso
Un codice hash è destinato all'inserimento efficiente e alla ricerca 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 chiave.
- Non inviare codici hash tra domini applicazioni o processi. In alcuni casi, i codici hash possono essere calcolati per ogni processo o per dominio applicazione.
- Non usare il codice hash invece di un valore restituito da una funzione di hashing crittografico se è necessario un hash crittografato. 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 testare 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 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 di valore non eseguono l'override GetHashCode, il ValueType.GetHashCode metodo della classe base usa la reflection per calcolare il codice hash in base ai valori dei campi del tipo. In altre parole, i tipi di valore i cui campi hanno valori uguali hanno codici hash uguali. Per altre informazioni sull'override GetHashCode, vedere la sezione "Note per ereditatori".
Avviso
Se si esegue l'override del metodo, è anche necessario eseguire l'override GetHashCode Equalsdi e viceversa. Se il metodo sottoposto a override Equals restituisce true
quando due oggetti vengono testati per l'uguaglianza, il GetHashCode metodo sottoposto 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 di Hashtable classe.
Note per la Windows Runtime
Quando si chiama il metodo in una classe nella Windows Runtime, fornisce il comportamento predefinito per le classi che non eseguono l'override GetHashCode GetHashCodedi . Questo è parte del supporto fornito dall'.NET Framework per l'Windows Runtime (vedere .NET Framework Supporto per le app Windows Store e Windows Runtime). Le classi nella Windows Runtime non ereditano Objecte attualmente non implementano un GetHashCodeoggetto . Tuttavia, sembrano avere ToStringmetodi , Equals(Object)e GetHashCode quando vengono usati nel codice C# o Visual Basic e il .NET Framework fornisce il comportamento predefinito per questi metodi.
Nota
Windows Runtime classi 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 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() di base solo se la classe derivata definisce l'uguaglianza di riferimento. L'implementazione predefinita di per i tipi di GetHashCode() riferimento restituisce un codice hash equivalente a quello restituito dal GetHashCode(Object) metodo . È possibile eseguire l'override GetHashCode() per i tipi di 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 cambi mentre l'oggetto è contenuto in una raccolta che si basa sul 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 siano state apportate 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 raggruppato. 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 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 è raggruppato in determinati intervalli (ad esempio, molti utenti potrebbero avere stringhe che contengono solo i caratteri ASCII inferiori di 128, anche se una stringa può contenere uno dei 65.535 caratteri Unicode).
La fornitura di una funzione hash valida in una classe può influire in modo significativo sulle prestazioni dell'aggiunta di tali oggetti a una tabella hash. In una tabella hash con chiavi che forniscono un'implementazione valida 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ò immettere dati che aumentano il numero di conflitti, che possono ridurre significativamente le prestazioni delle applicazioni che dipendono dalle tabelle hash, in base alle 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 inseriscono i dati da cui viene calcolato il codice hash.
Le classi derivate che eseguono l'override devono anche 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.