Object.GetHashCode Método
Definición
Importante
Parte de la información hace referencia a la versión preliminar del producto, que puede haberse modificado sustancialmente antes de lanzar la versión definitiva. Microsoft no otorga ninguna garantía, explícita o implícita, con respecto a la información proporcionada aquí.
Actúa como 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
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 tiene 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 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
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 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
En el ejemplo anterior se devuelve el mismo código hash para (n1, n2) y (n2, n1), por lo que podría generar más colisiones de las que son deseables. En .NET 5+, la solución recomendada es usar HashCode.Combine. Evita el problema de simetría y genera un código hash bien distribuido sin la sobrecarga de crear un 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
Comentarios
El GetHashCode método proporciona un código hash para algoritmos que necesitan comprobaciones rápidas de la igualdad de objetos. 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 .
Note
Para obtener información sobre cómo se usan los códigos hash en tablas hash y para algunos algoritmos de código hash adicionales, vea la entrada función hash en Wikipedia.
Dos objetos que son iguales devuelven códigos hash que son iguales. Sin embargo, la inversa no es cierta: 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 método GetHashCode y el valor que devuelve este método puede diferir entre las implementaciones y las plataformas de .NET, como entre plataformas de 32 y 64 bits. Por estos motivos, no use la implementación predeterminada de este método como identificador de objeto único con fines hash. A continuación se muestran dos consecuencias:
- 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.
Warning
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 los almacene en las 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 desiguales 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. Es decir, 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 reemplazar GetHashCode, vea la sección "Notes to Inheritors" (Notas para heredar).
Warning
Si invalida el método GetHashCode , también debe invalidar Equals y viceversa. Si el método invalidado Equals devuelve true cuando se prueban dos objetos para obtener 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 GetHashCode, puede especificar un proveedor de código hash proporcionando una implementación de IEqualityComparer a una de las sobrecargas del constructor de la clase Hashtable.
Notas a los desarrolladores de herederos
Se usa una función hash 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.
Para 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, puede pensar 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.
Para 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, vea 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 grandes modificaciones en el código hash resultante para obtener el mejor rendimiento de la tabla hash.
Las funciones hash deben ser baratas 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 razonablemente aleatoriamente, incluso cuando la entrada está agrupada 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 un tiempo constante (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 tablas hash, en las condiciones siguientes:
Cuando las funciones hash producen colisiones frecuentes.
Cuando una gran proporción de objetos de una tabla hash generan 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.