Object.GetHashCode Método

Definição

Serve como função de hash predefinida.

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

Devoluções

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 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 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, como 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 devolve o mesmo código hash para (n1, n2) e (n2, n1), podendo assim gerar mais colisões do que o desejável. No .NET 5+, a solução recomendada é usar HashCode.Combine. Evita o problema da simetria e produz um código 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

Observações

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

Dois objetos que são iguais retornam códigos hash que são iguais. No entanto, o inverso não é verdadeiro: códigos hash iguais não implicam igualdade de objetos, porque objetos diferentes (desiguais) podem ter códigos hash idênticos. Além disso, .NET não garante a implementação padrão do método GetHashCode, e o valor que este método devolve pode variar entre implementações .NET e plataformas, 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. Daqui decorrem duas consequências:

  • Você não deve assumir que códigos 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, porque o mesmo objeto pode fazer hash entre domínios, processos e plataformas do aplicativo.

Warning

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 esta razão:

  • 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 chaves.
  • Não envie códigos hash entre domínios ou processos de aplicativos. Em alguns casos, os códigos hash podem ser calculados por processo ou por domínio de aplicativo.
  • Não use o código hash em vez de um valor retornado por uma função de hash criptográfico se precisar de um hash criptograficamente forte. Para hashes criptográficos, use uma classe derivada da System.Security.Cryptography.HashAlgorithm classe ou System.Security.Cryptography.KeyedHashAlgorithm .
  • Não teste a igualdade de códigos hash para determinar se dois objetos são iguais. (Objetos desiguais podem ter códigos hash idênticos.) Para testar a igualdade, chame o ReferenceEquals método ou Equals .

O GetHashCode método pode ser substituído por um tipo derivado. Se GetHashCode não for substituído, os códigos hash para tipos de referência serão calculados 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 hash idênticos. Se os tipos de valor não substituírem 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 hash iguais. Para obter mais informações sobre substituição GetHashCode, consulte a seção "Notas para herdeiros".

Warning

Se você substituir o GetHashCode método, você também deve substituir Equals, e 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 uma chave numa tabela de hash não fornece uma implementação útil de GetHashCode, pode especificar um fornecedor de código hash fornecendo uma implementação de IEqualityComparer a uma das sobrecargas do construtor da classe Hashtable.

Notas para Herdeiros

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

Para classes derivadas de Object, o GetHashCode método só pode delegar à implementação da classe GetHashCode() base se a classe derivada definir igualdade como igualdade de referência. A implementação padrão de GetHashCode() para os tipos de referência devolve um código hash equivalente ao devolvido pelo GetHashCode(Object) método. Podes sobrescrever GetHashCode() para tipos de referência imutáveis. De um modo geral, para tipos de referência mutáveis, só deve sobrescrever GetHashCode() se:

  • Podes calcular o código de hash a partir de campos que não são mutáveis; ou

  • Pode garantir que o código hash de um objeto mutável não altera enquanto o objeto está contido numa coleção que depende do seu código hash.

Caso contrário, pode pensar que o objeto mutável se perde na tabela hash. Se optar por sobrescrever GetHashCode() para um tipo de referência mutável, a sua documentação deve deixar claro que utilizadores do seu tipo não devem modificar os valores do objeto enquanto o objeto está armazenado numa tabela hash.

Para tipos de valor, GetHashCode() fornece uma implementação padrão de código hash que utiliza reflexão. Deves considerar sobressprevê-lo para melhor desempenho.

Para mais informações e exemplos que calculam códigos de hash de várias formas, consulte a secção Exemplos.

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

  • Se dois objetos se compararem como iguais, o GetHashCode() método para cada objeto deve devolver o mesmo valor. No entanto, se dois objetos não se compararem como iguais, os GetHashCode() métodos para os dois objetos não têm de devolver valores diferentes.

  • O GetHashCode() método para um objeto deve devolver consistentemente o mesmo código hash desde que não haja modificação no estado do objeto que determine o valor de retorno do método System.Object.Equals do objeto. Note-se que isto é verdade apenas para a execução atual de uma aplicação, e que um código hash diferente pode ser devolvido se a aplicação for executada novamente.

  • Para o melhor desempenho, uma função de hash deve gerar uma distribuição uniforme para todas as entradas, incluindo entradas fortemente agrupadas. 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 hash.

  • As funções de hash devem ser baratas de calcular.

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

Por exemplo, a implementação do GetHashCode() método fornecido pela String classe devolve códigos hash idênticos para valores de cadeia idênticos. Portanto, dois String objetos retornam o mesmo código de hash se representarem o mesmo valor de cadeia. Além disso, o método utiliza todos os caracteres da cadeia para gerar uma saída distribuída de forma razoavelmente aleatória, mesmo quando a entrada está agrupada em certos intervalos (por exemplo, muitos utilizadores podem ter cadeias que contêm apenas os 128 caracteres ASCII inferiores, mesmo que uma cadeia possa conter qualquer um dos 65.535 caracteres Unicode).

Fornecer uma boa função de hash numa classe pode afetar significativamente o desempenho da adição desses objetos a uma tabela de hash. Numa tabela de hash com chaves que fornecem uma boa implementação de uma função de hash, a procura de um elemento demora tempo constante (por exemplo, uma operação O(1)). Numa tabela de hash com uma implementação pobre 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 é o número de itens na tabela de hash). Um utilizador malicioso pode introduzir dados que aumentam o número de colisões, o que pode degradar significativamente o desempenho de aplicações dependentes de tabelas hash, sob as seguintes condições:

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

  • Quando uma grande proporção de objetos numa tabela de hash produz códigos de hash que são iguais ou aproximadamente iguais entre si.

  • Quando os utilizadores introduzem os dados a partir dos quais o código de hash é calculado.

As classes derivadas que sobrescrevem GetHashCode() também têm de sobrescrever Equals(Object) para garantir que dois objetos considerados iguais têm o mesmo código hash; caso contrário, o Hashtable tipo pode não funcionar corretamente.

Aplica-se a

Ver também