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

Um código hash para o objeto atual.

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 $"{pt2.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 desejável. No .NET 5+, a solução recomendada é usar HashCode.Combine. Ele evita o problema de simetria e produz um código de hash bem distribuído sem a sobrecarga de criar um Tuple objeto.

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 HashCode.Combine(x, y);
    }
}

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 output similar to the following.
// Note: HashCode.Combine results are not stable across .NET versions.
//       185727722
//       -363254492
[<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() =
        System.HashCode.Combine(x, y)

let pt = Point(5, 8)
printfn $"{pt.GetHashCode()}"

let pt2 = Point(8, 5)
printfn $"{pt2.GetHashCode()}"
// The example displays output similar to the following.
// Note: HashCode.Combine results are not stable across .NET versions.
//       185727722
//       -363254492
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 HashCode.Combine(x, 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         
' The example displays output similar to the following.
' Note: HashCode.Combine results are not stable across .NET versions.
'       185727722
'       -363254492

Comentários

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.

Note

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, .NET não garante a implementação padrão do método GetHashCode e o valor retornado por esse método pode ser diferente entre implementações e plataformas de .NET, como entre 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.

Notas aos Herdeiros

Uma função de 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 computados usando os valores dos campos estáticos.

Para classes derivadas, Objecto GetHashCode método pode delegar à implementação da classe GetHashCode() base somente se a classe derivada definir 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() os 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 dependa de seu código 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 de códigos de hash de computação 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 de 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 fortemente clusterizados. Uma implicação é que pequenas modificações no estado do objeto devem resultar em grandes modificações no código 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, procurar 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 hash iguais ou aproximadamente iguais uns aos outros.

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

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

Aplica-se a

Confira também