c# - ¿Es Object.GetHashCode() único para una referencia o un valor?
gethashcode c# override (6)
La documentación de MSDN en Object.GetHashCode () describe 3 reglas contradictorias sobre cómo debería funcionar el método.
- Si dos objetos del mismo tipo representan el mismo valor, la función hash debe devolver el mismo valor constante para cualquier objeto.
- Para obtener el mejor rendimiento, una función hash debe generar una distribución aleatoria para todas las entradas.
- La función hash debe devolver exactamente el mismo valor independientemente de los cambios que se realicen en el objeto.
Las reglas 1 y 3 son contradictorias para mí.
Does Object.GetHashCode () devuelve un número único basado en el valor de un objeto, o la referencia al objeto. Si anulo el método, puedo elegir qué usar, pero me gustaría saber qué se usa internamente si alguien sabe.
Las reglas 1 y 3 son contradictorias para mí.
Hasta cierto punto, lo son. La razón es simple: si un objeto está almacenado en una tabla hash y, al cambiar su valor, cambia su hash entonces la tabla hash ha perdido el valor y no puede encontrarlo de nuevo consultando la tabla hash. Es importante que, mientras que los objetos se almacenan en una tabla hash, conservan su valor hash.
Para darse cuenta de esto, a menudo es más simple hacer inmutables objetos lavables, evadiendo así todo el problema. Sin embargo, es suficiente hacer que solo esos campos sean inmutables y determinen el valor hash.
Considere el siguiente ejemplo:
struct Person {
public readonly string FirstName;
public readonly string Name;
public readonly DateTime Birthday;
public int ShoeSize;
}
La gente rara vez cambia su fecha de nacimiento y la mayoría de la gente nunca cambia su nombre (excepto cuando se casa). Sin embargo, el tamaño de su zapato puede crecer arbitrariamente, o incluso reducirse. Por lo tanto, es razonable identificar a las personas que usan su fecha de nacimiento y su nombre, pero no el tamaño de su zapato. El valor hash debe reflejar esto:
public int GetHashCode() {
return FirstName.GetHashCode() ^ Name.GetHashCode() ^ Birthday.GetHashCode();
}
Las Reglas 1 y 3 no son realmente una contradicción.
Para un tipo de referencia, el código hash se deriva de una referencia al objeto: cambie la propiedad de un objeto y la referencia sea la misma.
Para los tipos de valor, el código hash se deriva del valor, cambia una propiedad de un tipo de valor y se obtiene una instancia completamente nueva del tipo de valor.
No estoy seguro de a qué documentación de MSDN se refiere. Mirando la documentación actual en Object.GetHashCode ( http://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx ) proporciona las siguientes "reglas":
Si dos objetos se comparan como iguales, el método GetHashCode para cada objeto debe devolver el mismo valor. Sin embargo, si dos objetos no se pueden comparar como iguales, los métodos GetHashCode para los dos objetos no tienen que devolver valores diferentes.
El método GetHashCode para un objeto debe devolver consistentemente el mismo código hash siempre que no haya ninguna modificación en el estado del objeto que determine el valor de retorno del método Equals del objeto. Tenga en cuenta que esto es cierto solo para la ejecución actual de una aplicación, y que se puede devolver un código hash diferente si la aplicación se ejecuta nuevamente.
Para obtener el mejor rendimiento, una función hash debe generar una distribución aleatoria para todas las entradas.
Si se refiere al segundo punto, las frases clave aquí son "siempre que no haya modificaciones en el estado del objeto" y "verdadero solo para la ejecución actual de una aplicación".
También de la documentación,
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 son generalmente específicas para cada tipo y deben usar al menos uno de los campos de instancia como entrada. [El énfasis añadido es mío. ]
En cuanto a la implementación real, establece claramente que las clases derivadas pueden diferir a la implementación Object.GetHashCode si y solo si esa clase derivada define la igualdad de valor como igualdad de referencia y el tipo no es un tipo de valor. En otras palabras, la implementación predeterminada de Object.GetHashCode se basará en la igualdad de referencia ya que no hay campos de instancia reales para usar y, por lo tanto, no garantiza valores de devolución únicos para diferentes objetos. De lo contrario, su implementación debería ser específica para su tipo y debería usar al menos uno de sus campos de instancia. Como ejemplo, la implementación de String.GetHashCode devuelve códigos hash idénticos para valores de cadena idénticos, por lo que dos objetos String devuelven el mismo código hash si representan el mismo valor de cadena, y utiliza todos los caracteres de la cadena para generar ese valor hash.
Por defecto, lo hace en función de la referencia al objeto, pero eso significa que es exactamente el mismo objeto, por lo que ambos devolverían el mismo hash. Pero un hash debe basarse en el valor, como en el caso de la clase de cadena. "a" y "b" tendrían un hash diferente, pero "a" y "a" devolverían el mismo hash.
No puedo saber con certeza cómo se implementa Object.GetHashCode en .NET Framework real , pero en Rotor usa el índice SyncBlock para el objeto como código hash. Hay algunas publicaciones en el blog en la web, sin embargo, la mayoría son de 2005.
Una muy buena explicación sobre cómo manejar GetHashCode
(más allá de las reglas de Microsoft) se encuentra en Eric Lipperts (co-diseñador de C #) Blog con el artículo " Pautas y reglas para GetHashCode ". No es una buena práctica agregar hipervínculos aquí (ya que pueden ser no válidos) pero vale la pena, y siempre que la información de arriba probablemente la encuentre en caso de que se pierda el hipervínculo.