2008-08-29 18 views
21

La documentación de MSDN en Object.GetHashCode() describe 3 reglas contradictorias sobre cómo debería funcionar el método.¿Object.GetHashCode() es exclusivo de una referencia o un valor?

  1. Si dos objetos del mismo tipo representan el mismo valor, la función hash debe devolver el mismo valor constante para cualquier objeto.
  2. Para obtener el mejor rendimiento, una función hash debe generar una distribución aleatoria para todas las entradas.
  3. La función hash debe devolver exactamente el mismo valor independientemente de los cambios que se realicen en el objeto.

Reglas 1 & 3 son contradictorias para mí.

¿El Object.GetHashCode() devolver 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.

Respuesta

27

Reglas 1 & 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 realizar esto, a menudo es más simple hacer objetos inastillables inmutables, evadiendo así todo el problema. Sin embargo, es suficiente hacer que solo esos campos sean inmutables y determinen el valor hash.

Consideremos el siguiente ejemplo:

struct Person { 
    public readonly string FirstName; 
    public readonly string Name; 
    public readonly DateTime Birthday; 

    public int ShoeSize; 
} 

La gente rara vez cambian su cumpleaños y la mayoría de la gente nunca cambia su nombre (excepto al casarse). 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(); 
} 
+0

Dado que todos los objetos son hashable en C# (GetHashCode() forma parte del tipo de objeto muy básico), que sugiere que hacer todos los objetos inmutables - no es muy práctico, no es así? – thewhiteambit

+1

@thewhiteambit No.Estoy sugiriendo que no todos los objetos son buenos candidatos para las teclas de tabla hash. Solo porque * pueden * ser hasheados no significa que * deberían * ser *. Y el hecho de que 'GetHashCode' es un método de la clase base' Object' es simplemente una mala decisión de diseño en el lenguaje C#. Además, mi respuesta no es que deba hacer que cada tipo de clave de la tabla hash sea inmutable, sino que hacerlo ayuda tremendamente. –

+0

Tienes razón, solo estaba señalando la frase "Para darme cuenta de esto, a menudo es más simple hacer inmanejables los objetos con capacidad de manipulación", y dado que todos los objetos son lavables (por mala elección de diseño) este intento haría todos los objetos con capacidad de carga (igual a todos objetos) inmutables. Pero supongo que no lo dijiste de esa manera. Probablemente solo quiso hacer que todos los objetos que quiera almacenar en Hash-Collections sean inmutables. – thewhiteambit

0

De forma predeterminada 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.

5

No estoy seguro de a qué documentación de MSDN se refiere. En cuanto a la documentación actual sobre Object.GetHashCode (http://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx) ofrece las siguientes "reglas":

  • Si dos objetos resultan ser 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 modificaciones en el estado del objeto que determina 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 el mejor rendimiento, una función hash debe generar una distribución al azar de todas las entradas.

Si se refiere a la segunda viñeta, las frases clave aquí son "siempre y cuando no hay ninguna modificación en el estado de objeto" y "cierto sólo para la ejecución actual de una aplicación".

También desde la documentación,

una función hash se utiliza 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 instancia como entrada. [El énfasis agregado es mío.]

En cuanto a la implementación real, se establece claramente que las clases derivadas pueden diferir a la aplicación Object.GetHashCode si y sólo si que clase derivada define la igualdad de valor a ser la igualdad de referencia y el tipo no es una 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.

+0

Esa fue la respuesta más detallada y confusa que jamás haya leído. Me dejó aún más confundido de lo que comencé. – bleepzter

4

Reglas 1 & 3 no son realmente una contradicción.

Para un tipo de referencia el código hash se deriva de una referencia al objeto - de cambio de propiedad de un objeto y la referencia es el mismo.

Para los tipos de valor del código hash se deriva del valor, cambie una propiedad de un tipo de valor y se obtiene una nueva instancia del tipo de valor.

+0

Esto tiene sentido. – bleepzter

0

No puedo estar seguro de cómo se implementa Object.GetHashCode en real .NET Framework, pero en Rotor usa el índice SyncBlock para el objeto como código hash. Hay algunas publicaciones en el blog en Internet, sin embargo, la mayoría son de 2005.

1

Una muy buena explicación sobre cómo manejar GetHashCode (más allá de las reglas de Microsoft) se da en Eric Lipperts (co-diseñador de C#) Blog con el artículo "Guidelines and rules for GetHashCode". No es una buena práctica agregar hipervínculos aquí (ya que pueden ser no válidos) pero este vale la pena, y siempre que la información anterior aún la encuentre en caso de que se pierda el hipervínculo.

Cuestiones relacionadas