Ha pasado mucho tiempo, pero, sin embargo, creo que aún es necesario dar una respuesta correcta a esta pregunta, incluidas las explicaciones sobre por qué y cómo. La mejor respuesta hasta ahora es la que cita exhaustivamente el MSDN: no intente hacer sus propias reglas, los muchachos de MS sabían lo que estaban haciendo.
Pero lo primero es lo primero: la guía como se cita en la pregunta es incorrecta.
Ahora los porqués, hay dos de ellos
Primero por qué : si el código hash se calcula de una manera, que no cambia durante la vida útil de un objeto, incluso si el objeto cambia, entonces rompería el contrato igual.
Recuerde: "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 comparan como iguales, los métodos GetHashCode para los dos objetos no tienen que devolver valores diferentes".
La segunda oración a menudo se malinterpreta como "La única regla es que, en el momento de la creación del objeto, el código hash de objetos iguales debe ser igual". Realmente no sé por qué, pero esa también es la esencia de la mayoría de las respuestas.
Piense en dos objetos que contienen un nombre, donde el nombre se usa en el método igual: Mismo nombre -> misma cosa. Crear instancia A: Nombre = Joe Crear instancia B: Nombre = Peter
Hashcode A y Hashcode B probablemente no serán lo mismo. ¿Qué pasaría ahora, cuando el nombre de la instancia B se cambia a Joe?
De acuerdo con la directriz de la pregunta, el código hash de B no cambiaría. El resultado de esto sería: A.Equals (B) ==> verdadero Pero al mismo tiempo: A.GetHashCode () == B.GetHashCode () ==> falso.
Pero exactamente este comportamiento está explícitamente prohibido por el contrato de igualdad y hashcode.
Segundo por qué : aunque es, por supuesto, cierto, que los cambios en el código hash podrían romper las listas hash y otros objetos que usan el código hash, lo contrario también es cierto. Si no se cambia el código hash, en el peor de los casos se obtendrán listas hash, donde muchos objetos diferentes tendrán el mismo código hash y, por lo tanto, estarán en el mismo bin hash; esto ocurre cuando los objetos se inicializan con un valor estándar, por ejemplo.
Ahora llegando a los cómo Bueno, a primera vista, parece haber una contradicción: de cualquier manera, el código se romperá. Pero ninguno de los problemas proviene de un hashcode modificado o no modificado.
La fuente de los problemas está bien descrita en el MSDN:
De la entrada de la tabla hash de MSDN:
Los objetos clave deben ser inmutables siempre que se utilicen como claves en la tabla hash.
Esto significa:
Cualquier objeto que cree un valor hash debe cambiar el valor hash, cuando el objeto cambia, pero no debe, absolutamente no debe, permitirse ningún cambio en sí mismo, cuando se usa dentro de un Hashtable (o cualquier otro objeto que use Hash, por supuesto) .
Primero, la forma más fácil, por supuesto, sería diseñar objetos inmutables solo para el uso en tablas hash, que se crearán como copias de los objetos normales y mutables cuando sea necesario. Dentro de los objetos inmutables, obviamente está bien almacenar en caché el código hash, ya que es inmutable.
En segundo lugar, ¿cómo? O bien, asigne al objeto una etiqueta "ahora hash hashed", asegúrese de que todos los datos del objeto sean privados, verifique el indicador en todas las funciones que pueden cambiar los datos de los objetos y arroje un dato de excepción si el cambio no está permitido (es decir, el indicador está configurado ) Ahora, cuando coloque el objeto en cualquier área con hash, asegúrese de establecer la bandera y, también, desarmar la bandera, cuando ya no sea necesaria. Para facilitar su uso, le aconsejo que configure el indicador automáticamente dentro del método "GetHashCode", de esta manera no se puede olvidar. Y la llamada explícita de un método "ResetHashFlag" se asegurará de que el programador tendrá que pensar, ya sea que esté permitido o no cambiar los datos de los objetos por ahora.
Bien, lo que también se debe decir: hay casos en los que es posible tener objetos con datos mutables, sin embargo, el código hash no cambia, cuando los datos de los objetos se cambian, sin violar el contrato de igual y código hash.
Sin embargo, esto requiere que el método equals no se base también en los datos mutables. Entonces, si escribo un objeto y creo un método GetHashCode que calcula un valor solo una vez y lo almacena dentro del objeto para devolverlo en llamadas posteriores, entonces debo, nuevamente: absolutamente debe, crear un método Equals, que usará valores almacenados para la comparación, de modo que A.Equals (B) nunca cambiará de falso a verdadero también. De lo contrario, el contrato se rompería. El resultado de esto generalmente será que el método Equals no tiene ningún sentido: no es la referencia original igual, pero tampoco es un valor igual. A veces, esto puede ser un comportamiento intencionado (es decir, registros de clientes), pero generalmente no lo es.
Por lo tanto, simplemente haga que el resultado de GetHashCode cambie, cuando los datos del objeto cambien, y si el uso del objeto dentro del hash usando listas u objetos es intencional (o simplemente posible), haga que el objeto sea inmutable o cree un indicador de solo lectura para usar para el vida útil de una lista hash que contiene el objeto.
(Por cierto: todo esto no es específico de C # o .NET: está en la naturaleza de todas las implementaciones de tabla hash, o más generalmente de cualquier lista indexada, que los datos de identificación de los objetos nunca deberían cambiar, mientras el objeto está en la lista Se producirá un comportamiento inesperado e impredecible si se rompe esta regla. En algún lugar, puede haber implementaciones de la lista, que supervisan todos los elementos dentro de la lista y reindexan automáticamente la lista, pero el rendimiento de esos seguramente será horrible en el mejor de los casos).