Metodo System.Object.GetHashCode
Questo articolo fornisce osservazioni supplementari alla documentazione di riferimento per questa API.
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 .
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 (diversi) possono avere codici hash identici. Inoltre, .NET non garantisce l'implementazione predefinita del GetHashCode metodo e il valore restituito da questo metodo può variare tra le implementazioni .NET, ad esempio versioni diverse di .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 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.
Avviso
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 GetHashCodedi , il metodo della classe base usa la 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 GetHashCodedi , vedere la sezione "Note per ereditatori".
Avviso
Se si esegue l'override del GetHashCode metodo , è necessario eseguire anche l'override Equalsdi 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 Windows Runtime
Quando chiami il GetHashCode metodo su una classe in Windows Runtime, fornisce il comportamento predefinito per le classi che non eseguono l'override GetHashCodedi . Questo è parte del supporto fornito da .NET per Windows Runtime (vedere Supporto .NET per le app di Windows Store e Windows Runtime). Le classi in Windows Runtime non ereditano Objecte attualmente non implementano .GetHashCode Tuttavia, sembrano avere ToStringmetodi , Equals(Object)e GetHashCode quando vengono usati nel codice C# o Visual Basic e .NET Framework fornisce il comportamento predefinito per questi metodi.
Nota
Le classi di Windows Runtime scritte in C# o Visual Basic possono eseguire l'override del GetHashCode metodo .
Esempi
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 $"{pt.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 può generare più conflitti di quanto siano auspicabili. Sono disponibili diverse 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 Tuple
oggetto può influire significativamente sulle prestazioni complessive di un'applicazione che archivia un numero elevato di oggetti nelle tabelle hash.
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 Tuple.Create(x, y).GetHashCode();
}
}
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 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 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 compressi anziché essere rimossi. Poiché i bit vengono scartati dagli operatori di spostamento sinistro sia in C# che in Visual Basic, è necessario creare un metodo di spostamento e ritorno a sinistra 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);
}
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 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 Example2
{
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 Point5
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 Point5 Then Return False
Dim p As Point5 = CType(obj, Point5)
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 Example2
Public Sub Main()
Dim pt As New Point5(5, 8)
Console.WriteLine(pt.GetHashCode())
pt = New Point5(8, 5)
Console.WriteLine(pt.GetHashCode())
End Sub
End Module
' The example displays the following output:
' 28
' 37