Object.GetHashCode Método

Definición

Sirve como la función hash predeterminada.

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

Devoluciones

Int32

Código hash para el objeto actual.

Ejemplos

Una de las formas más sencillas de calcular un código hash para un valor numérico que tenga el mismo o un intervalo menor que el Int32 tipo es simplemente devolver ese valor. En el ejemplo siguiente se muestra una implementación de este tipo para una Number estructura.

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

Con frecuencia, un tipo tiene varios campos de datos que pueden participar en la generación del código hash. Una manera de generar un código hash es combinar estos campos mediante una XOR (eXclusive OR) operación, como se muestra en el ejemplo siguiente.

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

En el ejemplo anterior se devuelve el mismo código hash para (n1, n2) y (n2, n1), por lo que puede generar más colisiones de las deseadas. Hay varias soluciones disponibles para que los códigos hash en estos casos no sean idénticos. Uno es devolver el código hash de un Tuple objeto que refleja el orden de cada campo. En el ejemplo siguiente se muestra una posible implementación que usa la Tuple<T1,T2> clase . Sin embargo, tenga en cuenta que la sobrecarga de rendimiento de crear instancias de un Tuple objeto puede afectar significativamente al rendimiento general de una aplicación que almacena un gran número de objetos en tablas 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

Una segunda solución alternativa implica ponderar los códigos hash individuales cambiando a la izquierda los códigos hash de campos sucesivos por dos o más bits. De forma óptima, los bits desplazados más allá del bit 31 deben encapsularse en lugar de descartarse. Dado que los operadores de desplazamiento izquierdo descartan bits en C# y Visual Basic, esto requiere crear un método de desplazamiento y ajuste a la izquierda como el siguiente:

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

A continuación, en el ejemplo siguiente se usa este método shift-and-wrap para calcular el código hash de la Point estructura usada en los ejemplos 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

Comentarios

Un código hash es un valor numérico que se usa para insertar e identificar un objeto en una colección basada en hash, como la Dictionary<TKey,TValue> clase, la Hashtable clase o un tipo derivado de la DictionaryBase clase . El GetHashCode método proporciona este código hash para los algoritmos que necesitan comprobaciones rápidas de la igualdad de objetos.

Nota

Para obtener información sobre cómo se usan los códigos hash en las tablas hash y para algunos algoritmos de código hash adicionales, vea la entrada Función hash en Wikipedia.

Dos objetos que son iguales a códigos hash devueltos que son iguales. Sin embargo, el valor inverso no es cierto: los códigos hash iguales no implican la igualdad de objetos, ya que los distintos objetos (distintos) pueden tener códigos hash idénticos. Además, .NET no garantiza la implementación predeterminada del GetHashCode método y el valor que devuelve este método puede diferir entre las implementaciones de .NET, como diferentes versiones de .NET Framework y .NET Core, y plataformas, como plataformas de 32 y 64 bits. Por estos motivos, no use la implementación predeterminada de este método como un identificador de objeto único con fines hash. Las dos consecuencias se derivan de esto:

  • No debe suponer que los códigos hash iguales implican la igualdad de objetos.

  • Nunca debe conservar o usar un código hash fuera del dominio de aplicación en el que se creó, ya que el mismo objeto puede aplicar un hash entre dominios de aplicación, procesos y plataformas.

Advertencia

Un código hash está diseñado para una inserción y búsqueda eficaces en colecciones basadas en una tabla hash. Un código hash no es un valor permanente. Por este motivo:

  • No serialice los valores de código hash ni almacénelos en bases de datos.
  • No use el código hash como clave para recuperar un objeto de una colección con claves.
  • No envíe códigos hash entre dominios de aplicación ni procesos. En algunos casos, los códigos hash se pueden calcular por proceso o por dominio de aplicación.
  • No use el código hash en lugar de un valor devuelto por una función hash criptográfica si necesita un hash criptográficomente seguro. Para los hash criptográficos, use una clase derivada de la System.Security.Cryptography.HashAlgorithm clase o System.Security.Cryptography.KeyedHashAlgorithm .
  • No pruebe la igualdad de códigos hash para determinar si dos objetos son iguales. (Los objetos distintos pueden tener códigos hash idénticos). Para probar la igualdad, llame al ReferenceEquals método o Equals .

Un tipo derivado puede invalidar el GetHashCode método. Si GetHashCode no se invalida, los códigos hash de los tipos de referencia se calculan llamando al Object.GetHashCode método de la clase base, que calcula un código hash basado en la referencia de un objeto; para obtener más información, vea RuntimeHelpers.GetHashCode. En otras palabras, dos objetos para los que el ReferenceEquals método devuelve true tienen códigos hash idénticos. Si los tipos de valor no invalidan GetHashCode, el ValueType.GetHashCode método de la clase base usa la reflexión para calcular el código hash en función de los valores de los campos del tipo. En otras palabras, los tipos de valor cuyos campos tienen valores iguales tienen códigos hash iguales. Para obtener más información sobre cómo invalidar GetHashCode, vea la sección "Notas a los herederas".

Advertencia

Si invalida el GetHashCode método , también debe invalidar Equalsy viceversa. Si el método invalidado Equals devuelve true cuando se prueban dos objetos para la igualdad, el método invalidado GetHashCode debe devolver el mismo valor para los dos objetos.

Si un objeto que se usa como clave en una tabla hash no proporciona una implementación útil de , puede especificar un proveedor de GetHashCodecódigo hash proporcionando una IEqualityComparer implementación a una de las sobrecargas del constructor de clase Hashtable .

Notas del Windows Runtime

Al llamar al GetHashCode método en una clase de la Windows Runtime, proporciona el comportamiento predeterminado para las clases que no invalidan GetHashCode. Esta es parte de la compatibilidad que proporciona el .NET Framework para el Windows Runtime (consulta .NET Framework compatibilidad con aplicaciones y Windows Runtime de la Tienda Windows). Las clases de la Windows Runtime no heredan Objecty actualmente no implementan .GetHashCode Sin embargo, parecen tener ToStringmétodos , Equals(Object)y GetHashCode cuando los usa en el código de C# o Visual Basic, y el .NET Framework proporciona el comportamiento predeterminado para estos métodos.

Nota

Windows Runtime clases escritas en C# o Visual Basic pueden invalidar el GetHashCode método .

Notas a los desarrolladores de herederos

Una función hash se usa para generar rápidamente un número (código hash) que corresponde al valor de un objeto . Las funciones hash suelen ser específicas de cada tipo y, para la unicidad, deben usar al menos uno de los campos de instancia como entrada. Los códigos hash no se deben calcular mediante los valores de los campos estáticos.

En el caso de las clases derivadas de Object, el GetHashCode método puede delegar en la implementación de la clase GetHashCode() base solo si la clase derivada define la igualdad para ser igualdad de referencia. La implementación predeterminada de para los tipos de GetHashCode() referencia devuelve un código hash equivalente al devuelto por el GetHashCode(Object) método . Puede invalidar GetHashCode() los tipos de referencia inmutables. En general, para los tipos de referencia mutables, solo debe invalidar GetHashCode() si:

  • Puede calcular el código hash desde campos que no son mutables; O
  • Puede asegurarse de que el código hash de un objeto mutable no cambia mientras el objeto está contenido en una colección que se basa en su código hash.

De lo contrario, es posible que piense que el objeto mutable se pierde en la tabla hash. Si decide invalidar GetHashCode() un tipo de referencia mutable, la documentación debe aclarar que los usuarios del tipo no deben modificar los valores de objeto mientras el objeto se almacena en una tabla hash.

En el caso de los tipos de valor, GetHashCode() proporciona una implementación de código hash predeterminada que usa la reflexión. Debe considerar la posibilidad de invalidarla para mejorar el rendimiento.

Para obtener más información y ejemplos que calculan códigos hash de varias maneras, consulte la sección Ejemplos.

Una función hash debe tener las siguientes propiedades:

  • Si dos objetos se comparan como iguales, el GetHashCode() método de cada objeto debe devolver el mismo valor. Sin embargo, si dos objetos no se comparan como iguales, los GetHashCode() métodos de los dos objetos no tienen que devolver valores diferentes.

  • El GetHashCode() método de un objeto debe devolver de forma coherente el mismo código hash siempre que no haya ninguna modificación en el estado del objeto que determine el valor devuelto del método System.Object.Equals del objeto. Tenga en cuenta que esto solo es cierto para la ejecución actual de una aplicación y que se puede devolver un código hash diferente si la aplicación se vuelve a ejecutar.

  • Para obtener el mejor rendimiento, una función hash debe generar una distribución uniforme para todas las entradas, incluida la entrada que está muy agrupada. Una implicación es que las pequeñas modificaciones en el estado del objeto deben dar lugar a modificaciones grandes en el código hash resultante para obtener el mejor rendimiento de la tabla hash.

  • Las funciones hash deben ser económicas para calcular.

  • El GetHashCode() método no debe producir excepciones.

Por ejemplo, la implementación del GetHashCode() método proporcionado por la String clase devuelve códigos hash idénticos para valores de cadena idénticos. Por lo tanto, dos String objetos devuelven el mismo código hash si representan el mismo valor de cadena. Además, el método usa todos los caracteres de la cadena para generar una salida distribuida aleatoriamente, incluso cuando la entrada se agrupa en determinados intervalos (por ejemplo, muchos usuarios pueden tener cadenas que contengan solo los 128 caracteres ASCII inferiores, aunque una cadena pueda contener cualquiera de los 65 535 caracteres Unicode).

Proporcionar una buena función hash en una clase puede afectar significativamente al rendimiento de agregar esos objetos a una tabla hash. En una tabla hash con claves que proporcionan una buena implementación de una función hash, la búsqueda de un elemento tarda constantemente (por ejemplo, una operación O(1). En una tabla hash con una implementación deficiente de una función hash, el rendimiento de una búsqueda depende del número de elementos de la tabla hash (por ejemplo, una operación O(n), donde n es el número de elementos de la tabla hash. Un usuario malintencionado puede introducir datos que aumentan el número de colisiones, lo que puede degradar significativamente el rendimiento de las aplicaciones que dependen de las tablas hash, en las siguientes condiciones:

  • Cuando las funciones hash producen colisiones frecuentes.

  • Cuando una gran proporción de objetos de una tabla hash produce códigos hash que son iguales o aproximadamente iguales entre sí.

  • Cuando los usuarios introducen los datos desde los que se calcula el código hash.

Las clases derivadas que invalidan GetHashCode() también deben invalidar Equals(Object) para garantizar que dos objetos considerados iguales tengan el mismo código hash; de lo contrario, es posible que el Hashtable tipo no funcione correctamente.

Se aplica a

Consulte también