ejemplo c# .net override equals gethashcode

gethashcode c# ejemplo



¿Anulando GetHashCode para objetos mutables? (5)

¿Cómo funciona eso si los campos en los que se basa son mutables?

No lo hace en el sentido de que el código hash cambiará a medida que cambia el objeto. Ese es un problema por todas las razones enumeradas en los artículos que lees. Desafortunadamente, este es el tipo de problema que normalmente solo aparece en casos de esquina. Así que los desarrolladores tienden a salirse con la suya del mal comportamiento.

Además, ¿qué sucede si deseo que las búsquedas de diccionarios, etc., se basen en la igualdad de referencia, no en mis iguales anulados?

Mientras implementes una interfaz como IEquatable<T> esto no debería ser un problema. La mayoría de las implementaciones de diccionarios elegirán un comparador de igualdad de una manera que use IEquatable<T> sobre Object.ReferenceEquals. Incluso sin IEquatable<T> , la mayoría de forma predeterminada llamará a Object.Equals (), que luego ingresará en su implementación.

Básicamente, en la mayoría de los códigos de ejecución, quiero igualdad de referencia y siempre uso == y no estoy anulando eso.

Si espera que sus objetos se comporten con igualdad de valores, debe anular == y! = Para imponer la igualdad de valores para todas las comparaciones. Los usuarios aún pueden usar Object.ReferenceEquals si realmente quieren igualdad de referencia.

Solía ​​suponer que el marco siempre usa == y no Igual para comparar cosas

Lo que usa el BCL ha cambiado un poco con el tiempo. Ahora, la mayoría de los casos que utilizan la igualdad tomarán una IEqualityComparer<T> y la usarán para la igualdad. En los casos en que no se especifica uno, usarán EqualityComparer<T>.Default para encontrar uno. En el peor de los casos, por defecto llamará a Object.Equals.

He leído sobre 10 preguntas diferentes sobre cuándo y cómo anular GetHashCode pero todavía hay algo que no entiendo. La mayoría de las implementaciones de GetHashCode se basan en los códigos hash de los campos del objeto, pero se ha citado que el valor de GetHashCode nunca debe cambiar durante la vida útil del objeto. ¿Cómo funciona eso si los campos en los que se basa son mutables? Además, ¿qué sucede si deseo que las búsquedas de diccionarios, etc., se basen en la igualdad de referencia, no en mis Equals anulados?

Principalmente estoy anulando Equals por la facilidad de la unidad de prueba de mi código de serialización, que asumo que serializar y deserializar (a XML en mi caso) mata la igualdad de referencia, así que quiero asegurarme de que al menos sea correcta por la igualdad de valores. ¿Es esta mala práctica anular a Equals en este caso? Básicamente, en la mayoría de los códigos de ejecución, quiero igualdad de referencia y siempre uso == y no estoy anulando eso. ¿Debo crear un nuevo método ValueEquals o algo en lugar de reemplazar a Equals ? Solía ​​suponer que el marco siempre usa == y no Equals para comparar cosas, por lo que pensé que era seguro anular a Equals ya que me parecía que su propósito era para si desea tener una segunda definición de igualdad que sea diferente de el operador == . De leer varias otras preguntas, parece que ese no es el caso.

EDITAR:

Parece que mis intenciones no estaban claras, lo que quiero decir es que el 99% de las veces quiero la igualdad de referencia, el comportamiento predeterminado, sin sorpresas. Para casos muy raros, quiero tener igualdad de valores, y quiero solicitar explícitamente la igualdad de valores utilizando .Equals lugar de == .

Cuando hago esto, el compilador recomienda que también anule GetHashCode , y así surgió esta pregunta. Parecía que hay objetivos contradictorios para GetHashCode cuando se aplica a objetos mutables, siendo estos:

  1. Si a.Equals(b) entonces a.GetHashCode() debe == b.GetHashCode() .
  2. El valor de a.GetHashCode() nunca debe cambiar durante la vida útil de a .

Estos parecen contradecir naturalmente cuando un objeto mutable, porque si el estado del objeto cambia, esperamos que cambie el valor de .Equals() , lo que significa que GetHashCode debe cambiar para que coincida con el cambio en .Equals() , pero GetHashCode no debe cambio.

¿Por qué parece haber esta contradicción? ¿Estas recomendaciones no están destinadas a aplicarse a objetos mutables? Probablemente asumido, pero valdría la pena mencionar que me refiero a clases y no a estructuras.

Resolución:

Estoy marcando JaredPar como aceptado, pero principalmente por la interacción de los comentarios. Para resumir lo que he aprendido de esto es que la única forma de lograr todos los objetivos y evitar posibles comportamientos extravagantes en los casos de borde es anular solo Equals y GetHashCode basados ​​en campos inmutables, o implementar IEquatable . Este tipo de disminución de la utilidad de los Equals para los tipos de referencia parece disminuir, ya que, por lo que he visto, la mayoría de los tipos de referencia no tienen campos inmutables a menos que estén almacenados en una base de datos relacional para identificarlos con sus claves principales.


El tema subyacente aquí es cómo identificar mejor los objetos. Usted menciona la serialización / deserialización que es importante porque la integridad referencial se pierde en ese proceso.

La respuesta corta, es que los objetos deben identificarse de manera única por el conjunto más pequeño de campos inmutables que se pueden usar para hacerlo. Estos son los campos que debe usar al anular GetHashCode y Equals.

Para las pruebas, es perfectamente razonable definir las aserciones que necesite, por lo general no están definidas en el tipo en sí, sino como métodos de utilidad en el conjunto de pruebas. Tal vez un TestSuite.AssertEquals (MyClass, MyClass)?

Tenga en cuenta que GetHashCode y Equals deberían trabajar juntos. GetHashCode debe devolver el mismo valor para dos objetos si son iguales. Igual debería devolver verdadero si y solo si dos objetos tienen el mismo código hash. (Tenga en cuenta que es posible que dos objetos no sean iguales pero puedan devolver el mismo código hash). Hay un montón de páginas web que abordan este tema de frente, solo con Google.


No sé acerca de C #, por ser un noob relativo a esto, pero en Java, si reemplaza a equals () también necesita reemplazar hashCode () para mantener el contrato entre ellos (y viceversa) ... Y Java También tiene la misma captura 22; básicamente forzando el uso de campos inmutables ... Pero esto es un problema solo para las clases que se usan como clave de hash, y Java tiene implementaciones alternativas para todas las colecciones basadas en hash ... que quizás no sean tan rápidas, pero lo hacen de manera efectiva le permite usar un objeto mutable como clave ... es simplemente (generalmente) mal visto como un "diseño deficiente".

Y siento la necesidad de señalar que este problema fundamental es atemporal ... Ha existido desde que Adam era un niño.

He trabajado en el código Fortran que es más antiguo que yo (tengo 36 años), que se rompe cuando se cambia un nombre de usuario (como cuando una niña se casa o se divorcia ;-) ... Así es la ingeniería. La solución adoptada fue : El "método" GetHashCode recuerda el hashCode calculado previamente, recalcula el hashCode (es decir, un marcador virtual isDirty) y si los campos clave han cambiado, devuelve un valor nulo. Esto hace que el caché elimine al usuario "sucio" (llamando a otro GetPreviousHashCode) y luego el caché devuelve un valor nulo, lo que hace que el usuario vuelva a leer desde la base de datos. Un truco interesante y valioso; incluso si lo digo yo mismo ;-)

Intercambiaré la mutabilidad (solo deseable en casos de esquina) por O (1) acceso (deseable en todos los casos). Bienvenido a la ingeniería; La tierra del compromiso informado.

Aclamaciones. Keith.


Si tiene un objeto mutable, no tiene mucho sentido anular el método GetHashCode, ya que realmente no puede usarlo. Se utiliza, por ejemplo, en las colecciones Dictionary y HashSet para colocar cada elemento en un contenedor. Si cambia el objeto mientras se usa como clave en la colección, el código hash ya no coincide con el grupo en el que se encuentra el objeto, por lo que la colección no funciona correctamente y es posible que nunca más encuentre el objeto.

Si desea que la búsqueda no use el método GetHashCode o Equals de la clase, siempre puede proporcionar su propia implementación de IEqualityComparer para usar en su lugar cuando cree el Dictionary .

El método Equals está destinado a la igualdad de valores, por lo que no es incorrecto implementarlo de esa manera.


Wow, en realidad son varias preguntas en una :-). Así que uno tras otro:

se ha citado que el valor de GetHashCode nunca debe cambiar durante la vida útil del objeto. ¿Cómo funciona eso si los campos en los que se basa son mutables?

Este consejo común está pensado para el caso en el que desee utilizar su objeto como clave en un HashTable / diccionario, etc. HashTables generalmente requiere que el hash no cambie, porque lo usan para decidir cómo almacenar y recuperar la clave. Si el hash cambia, la HashTable probablemente ya no encontrará su objeto.

Para citar los documentos de la interfaz del Mapa de Java:

Nota: se debe tener mucho cuidado si los objetos mutables se utilizan como claves de mapa. El comportamiento de un mapa no se especifica si el valor de un objeto se cambia de una manera que afecta a las comparaciones iguales, mientras que el objeto es una clave en el mapa.

En general, es una mala idea usar cualquier tipo de objeto mutable como una clave en una tabla hash: ni siquiera está claro qué debería suceder si una clave cambia después de que se haya agregado a la tabla hash. ¿Debería la tabla hash devolver el objeto almacenado mediante la clave antigua, o mediante la nueva clave, o mediante ambas?

Entonces, el verdadero consejo es: solo use objetos inmutables como claves, y asegúrese de que su código hash nunca cambie tampoco (lo que generalmente es automático si el objeto es inmutable).

Además, ¿qué sucede si deseo que las búsquedas de diccionarios, etc., se basen en la igualdad de referencia, no en mis iguales anulados?

Bueno, encuentra una implementación de diccionario que funcione así. Pero los diccionarios de la biblioteca estándar utilizan el código de hash e iguales, y no hay forma de cambiar eso.

Principalmente estoy anulando Equals por la facilidad de la unidad de prueba de mi código de serialización, que asumo que serializar y deserializar (a XML en mi caso) mata la igualdad de referencia, así que quiero asegurarme de que al menos sea correcta por la igualdad de valores. ¿Es esta mala práctica anular a Equals en este caso?

No, me parece perfectamente aceptable. Sin embargo, no debe usar objetos como claves en un diccionario / tabla hash, ya que son mutables. Véase más arriba.