Object.GetHashCode Método

Definição

Serve como a função de hash padrão.

public:
 virtual int GetHashCode();
public virtual int GetHashCode ();
abstract member GetHashCode : unit -> int
override this.GetHashCode : unit -> int
Public Overridable Function GetHashCode () As Integer

Retornos

Int32

Um código hash do objeto atual.

Exemplos

Uma das maneiras mais simples de calcular um código hash para um valor numérico que tenha o mesmo ou um intervalo menor do 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 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

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 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

O exemplo anterior retorna o mesmo código de hash para (n1, n2) e (n2, n1) e, portanto, pode gerar mais colisões do que desejável. 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 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

Uma segunda solução alternativa envolve ponderar os códigos de hash individuais deslocando os códigos de hash de campos sucessivos em dois ou mais bits. O ideal é que os bits deslocados além do bit 31 devem ser encapsulados em vez de descartados. Como os bits são descartados pelos operadores de turno esquerdo em C# e Visual Basic, isso requer a criação de um método shift-and-wrap à 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 Point seguir usa esse método shift-and-wrap para calcular o código hash da estrutura 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 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

Comentários

Um código hash é um valor numérico que é 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. O GetHashCode método fornece esse código de hash para algoritmos que precisam de verificações rápidas de igualdade de objetos.

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 códigos de hash de retorno iguais 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 hash idênticos. Além disso, o .NET não garante a implementação padrão do GetHashCode método e o valor retornado por esse método 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 se seguem a partir disso:

  • Você não deve assumir que códigos de hash iguais implicam igualdade de objetos.

  • 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 hash entre domínios, processos e plataformas de 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 esse 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 calculados 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 classe ou System.Security.Cryptography.KeyedHashAlgorithm da System.Security.Cryptography.HashAlgorithm classe.
  • Não teste a igualdade de códigos de hash para determinar se dois objetos são iguais. (Objetos desiguais podem ter códigos de hash idênticos.) Para testar a igualdade, chame o método ou Equals o ReferenceEquals método.

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 calculados chamando o Object.GetHashCode método da classe base, que computa 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 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 substituição GetHashCode, consulte a seção "Anotações aos Herdadores".

Aviso

Se você substituir o GetHashCode método, também deverá substituir Equalse vice-versa. Se o método substituído Equals retornar true quando dois objetos forem testados para igualdade, o 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, você poderá especificar um provedor de GetHashCodecódigo hash fornecendo uma IEqualityComparer implementação para uma das sobrecargas do Hashtable construtor de classe.

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 Framework fornece para o Windows Runtime (consulte .NET Framework Suporte para aplicativos da Windows Store e Windows Runtime). As classes no Windows Runtime não herdam Objecte atualmente não implementam um GetHashCode. No entanto, eles parecem terToString, 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

Windows Runtime classes escritas em C# ou Visual Basic podem substituir o GetHashCode método.

Notas aos Herdeiros

Uma função hash é usada para gerar rapidamente um número (código hash) que corresponde ao valor de um objeto. As funções de hash geralmente são específicas para cada tipo e, para exclusividade, devem usar pelo menos um dos campos de instância como entrada. Os códigos de hash não devem ser calculados usando os valores de campos estáticos.

Para classes derivadas, Objecto GetHashCode método pode delegar à implementação da classe GetHashCode() base somente se a classe derivada definir a igualdade como igualdade de referência. A implementação padrão de tipos de GetHashCode() referência retorna um código hash equivalente ao retornado pelo GetHashCode(Object) método. Você pode substituir GetHashCode() tipos de referência imutáveis. Em geral, para tipos de referência mutáveis, você só deverá substituir GetHashCode() se:

  • Você pode calcular o código hash de campos que não são mutáveis; Ou
  • Você pode garantir que o código hash de um objeto mutável não seja alterado enquanto o objeto estiver contido em uma coleção que depende de seu código de hash.

Caso contrário, você pode pensar que o objeto mutável é perdido na tabela de hash. Se você optar por substituir GetHashCode() um tipo de referência mutável, sua documentação deverá deixar claro que os usuários do seu tipo não devem modificar valores de objeto enquanto o objeto estiver armazenado em uma tabela de hash.

Para tipos de valor, GetHashCode() fornece uma implementação de código hash padrão que usa reflexão. Você deve considerar substituí-lo por um melhor desempenho.

Para obter mais informações e exemplos que computam códigos de hash de várias maneiras, consulte a seção Exemplos.

Uma função de hash deve ter as seguintes propriedades:

  • Se dois objetos forem comparados como iguais, o GetHashCode() método para cada objeto deverá retornar o mesmo valor. No entanto, se dois objetos não forem comparados como iguais, os GetHashCode() métodos para os dois objetos não precisarão retornar valores diferentes.

  • O GetHashCode() método para um objeto deve retornar consistentemente o mesmo código hash, desde que não haja nenhuma modificação no estado do objeto que determine o valor retornado do método System.Object.Equals do objeto. Observe que isso é verdadeiro apenas para a execução atual de um aplicativo e que um código hash diferente pode ser retornado se o aplicativo for executado novamente.

– Para obter o melhor desempenho, uma função de hash deve gerar uma distribuição uniforme para todas as entradas, incluindo entradas que estão fortemente clusterizados. Uma implicação é que pequenas modificações no estado do objeto devem resultar em grandes modificações no código de hash resultante para o melhor desempenho da tabela de hash.

  • As funções de hash devem ser baratas para computação.

  • O GetHashCode() método não deve gerar exceções.

Por exemplo, a implementação do GetHashCode() método fornecido pela String classe retorna códigos de hash idênticos para valores de cadeia de caracteres idênticos. Portanto, dois String objetos retornarão o mesmo código hash se representarem o mesmo valor de cadeia de caracteres. Além disso, o método usa todos os caracteres na cadeia de caracteres para gerar uma saída distribuída razoavelmente aleatoriamente, mesmo quando a entrada é clusterizado em determinados intervalos (por exemplo, muitos usuários podem ter cadeias de caracteres que contêm apenas os 128 caracteres ASCII inferiores, mesmo que uma cadeia de caracteres possa conter qualquer um dos 65.535 caracteres Unicode).

Fornecer uma boa função de hash em uma classe pode afetar significativamente o desempenho da adição desses objetos a uma tabela de hash. Em uma tabela de hash com chaves que fornecem uma boa implementação de uma função de hash, pesquisar um elemento leva tempo constante (por exemplo, uma operação O(1). Em uma tabela de hash com uma implementação ruim de uma função de hash, o desempenho de uma pesquisa depende do número de itens na tabela de hash (por exemplo, uma operação O(n), onde n está o número de itens na tabela de hash). Um usuário mal-intencionado pode inserir dados que aumentam o número de colisões, o que pode prejudicar significativamente o desempenho de aplicativos que dependem de tabelas de hash, nas seguintes condições:

  • Quando as funções de hash produzem colisões frequentes.

  • Quando uma grande proporção de objetos em uma tabela de hash produz códigos de hash iguais ou aproximadamente iguais uns aos outros.

  • Quando os usuários inserem os dados dos quais o código hash é calculado.

Classes derivadas que substituem GetHashCode() também devem substituir Equals(Object) para garantir que dois objetos considerados iguais tenham o mesmo código de hash; caso contrário, o Hashtable tipo pode não funcionar corretamente.

Aplica-se a

Confira também