Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Este artigo fornece comentários complementares à documentação de referência para esta API.
O GetHashCode método fornece um código hash para algoritmos que precisam de verificações rápidas de igualdade de objetos. Um código hash é um valor numérico usado para inserir e identificar um objeto em uma coleção baseada em hash, como a Dictionary<TKey,TValue> classe, a Hashtable classe ou um tipo derivado da DictionaryBase classe.
Observação
Para obter informações sobre como os códigos de hash são usados em tabelas de hash e para alguns algoritmos de código hash adicionais, consulte a entrada função hash na Wikipédia.
Dois objetos que são iguais retornam códigos de hash que são iguais. No entanto, o inverso não é verdadeiro: códigos de hash iguais não implicam igualdade de objetos, pois objetos diferentes (desiguais) podem ter códigos de hash idênticos. Além disso, o .NET não garante a implementação padrão do GetHashCode método e o valor que esse método retorna pode ser diferente entre implementações do .NET, como versões diferentes do .NET Framework e do .NET Core, e plataformas, como plataformas de 32 bits e 64 bits. Por esses motivos, não use a implementação padrão desse método como um identificador de objeto exclusivo para fins de hash. Duas consequências decorrem disso.
- Você não deve assumir que códigos de hash iguais implicam igualdade de objeto.
- Você nunca deve persistir ou usar um código hash fora do domínio do aplicativo no qual ele foi criado, pois o mesmo objeto pode fazer hash entre domínios, processos e plataformas do aplicativo.
Aviso
Um código hash destina-se à inserção e pesquisa eficientes em coleções baseadas em uma tabela de hash. Um código hash não é um valor permanente. Por este motivo:
- Não serialize valores de código hash nem armazene-os em bancos de dados.
- Não use o código hash como a chave para recuperar um objeto de uma coleção com chave.
- Não envie códigos de hash entre domínios ou processos do aplicativo. Em alguns casos, os códigos de hash podem ser computados por processo ou domínio por aplicativo.
- Não use o código hash em vez de um valor retornado por uma função de hash criptográfica se você precisar de um hash criptograficamente forte. Para hashes criptográficos, use uma classe derivada da System.Security.Cryptography.HashAlgorithm ou da System.Security.Cryptography.KeyedHashAlgorithm classe.
- Não teste a igualdade de códigos de hash para determinar se dois objetos são iguais. (Objetos diferentes podem ter códigos de hash idênticos.) Para testar a igualdade, chame o método ReferenceEquals ou Equals.
O GetHashCode método pode ser substituído por um tipo derivado. Se GetHashCode não for substituído, os códigos de hash para tipos de referência serão computados chamando o Object.GetHashCode método da classe base, que calcula um código hash com base na referência de um objeto; para obter mais informações, consulte RuntimeHelpers.GetHashCode. Em outras palavras, dois objetos para os quais o ReferenceEquals método retorna true têm códigos de hash idênticos. Se os tipos de valor não substituirem GetHashCode, o ValueType.GetHashCode método da classe base usará reflexão para calcular o código hash com base nos valores dos campos do tipo. Em outras palavras, os tipos de valor cujos campos têm valores iguais têm códigos de hash iguais. Para obter mais informações sobre a substituição GetHashCode, consulte a seção "Anotações aos Herdadores".
Aviso
Se você substituir o método GetHashCode, também deverá substituir Equals e vice-versa. Se o método substituído Equals retornar true quando dois objetos forem testados para igualdade, seu método substituído GetHashCode deverá retornar o mesmo valor para os dois objetos.
Se um objeto usado como chave em uma tabela de hash não fornecer uma implementação útil de GetHashCode, você poderá especificar um provedor de código hash fornecendo uma implementação de IEqualityComparer para uma das sobrecargas do construtor da classe Hashtable.
Anotações para o Windows Runtime
Quando você chama o GetHashCode método em uma classe no Windows Runtime, ele fornece o comportamento padrão para classes que não substituem GetHashCode. Isso faz parte do suporte que o .NET fornece para o Windows Runtime (consulte o suporte do .NET para aplicativos da Windows Store e o Windows Runtime). As classes no Windows Runtime não herdam Object e atualmente não implementam uma GetHashCode. No entanto, eles parecem ter ToString, Equals(Object)e GetHashCode métodos quando você os usa em seu código C# ou Visual Basic, e o .NET Framework fornece o comportamento padrão para esses métodos.
Observação
As classes do Windows Runtime escritas em C# ou Visual Basic podem substituir o GetHashCode método.
Exemplos
Uma das maneiras mais simples de calcular um código hash para um valor numérico que tenha o mesmo intervalo ou menor que o Int32 tipo é simplesmente retornar esse valor. O exemplo a seguir mostra essa implementação para uma Number estrutura.
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
Frequentemente, um tipo tem vários campos de dados que podem participar na geração do código hash. Uma maneira de gerar um código hash é combinar esses campos usando uma XOR (eXclusive OR) operação, conforme mostrado no exemplo a seguir.
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
O exemplo anterior retorna o mesmo código hash para (n1, n2) e (n2, n1) e, portanto, pode gerar mais colisões do que são desejáveis. Várias soluções estão disponíveis para que os códigos de hash nesses casos não sejam idênticos. Uma delas é retornar o código hash de um Tuple objeto que reflete a ordem de cada campo. O exemplo a seguir mostra uma possível implementação que usa a Tuple<T1,T2> classe. Observe, porém, que a sobrecarga de desempenho da instanciação de um Tuple objeto pode afetar significativamente o desempenho geral de um aplicativo que armazena um grande número de objetos em tabelas de 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
Uma segunda solução alternativa envolve a ponderação dos códigos de hash individuais deslocando os códigos de hash de campos sucessivos por dois ou mais bits. Idealmente, os bits deslocados para além do bit 31 devem ser encapsulados em vez de descartados. Como os bits são descartados pelos operadores de deslocamento à esquerda no C# e no Visual Basic, isso requer a criação de um método de deslocamento com rotação à esquerda, como o seguinte:
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
O exemplo a seguir usa o método shift-and-wrap para calcular o código hash da estrutura Point usada nos exemplos anteriores.
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