Pregunta

He leído acerca de 10 preguntas diferentes sobre cuándo y cómo reemplazar GetHashCode pero todavía hay algo que no me acababa de conseguir.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 del objeto.¿Cómo funciona si los campos que se basa en son mutables?También ¿qué debo hacer si quiero diccionario de búsquedas, etc a estar basada en la igualdad de referencia no es mi anulado Equals?

Estoy principalmente primordial Equals para la facilidad de la unidad de pruebas de mi código de serialización que supongo serializar y deserializar (XML en mi caso) mata a la igualdad de referencia así que quiero asegurarme de que al menos es correcto por la igualdad de valor.Es esta mala práctica para reemplazar Equals en este caso?Básicamente, en la mayor parte de la ejecución de código deseo de referencia de la igualdad y de la que siempre uso == y no estoy primordial que.Debo crear un nuevo método ValueEquals o algo en lugar de reemplazar Equals?Yo solía asumir que el marco siempre utiliza == y no Equals para comparar las cosas y, por eso pensé que era seguro para reemplazar Equals ya me parecía a mí como su propósito era que si usted quiere tener un 2º definición de igualdad que es diferente de la == operador.De la lectura de varias otras preguntas, aunque parece que no es el caso.

EDITAR:

Parece que mis intenciones eran claras, lo que quiero decir es que el 99% del tiempo, quiero el viejo y simple referencia de la igualdad, el comportamiento predeterminado, sin sorpresas.Para casos muy raros, quiero tener el valor de la igualdad, y quiero solicitar explícitamente la igualdad de valor mediante el uso de .Equals en lugar de ==.

Cuando hago esto el compilador recomienda anular GetHashCode así, y así es como esta pregunta se acercó.Parecía como que hay que contradicen los objetivos para GetHashCode cuando se aplica a objetos mutables, los cuales son:

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

Estos parecen naturalmente, contradiciendo cuando un objeto mutable, porque si el estado de un objeto cambia, se espera que el valor de .Equals() a cambio, lo que significa que GetHashCode debe cambiar para que coincida con el cambio en la .Equals(), pero GetHashCode no debe cambiar.

Por qué no parece ser esta contradicción?Son estas recomendaciones no pretenden aplicar a los objetos mutables?Probablemente asumido, pero podría ser vale la pena mencionar que me estoy refiriendo a las clases no structs.

Resolución:

Estoy marcado JaredPar aceptado, pero principalmente por los comentarios de la interacción.Para resumir lo que he aprendido de esto es que la única manera de alcanzar todos los objetivos y para evitar posibles peculiar comportamiento en el borde de los casos es sólo reemplazar Equals y GetHashCode basado en la inmutable campos, o implementar IEquatable.Este tipo de parece disminuir la utilidad de primordial Equals para los tipos de referencia, como por lo que he visto la mayoría de los tipos de referencia por lo general no tienen inmutable campos, a menos que se almacenan en una base de datos relacional para identificarlos con sus claves principales.

¿Fue útil?

Solución

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

No lo hace en el sentido de que el código hash cambiará a medida que cambia el objeto. Eso es un problema para todos los motivos enumerados en los artículos que lees. Por desgracia, este es el tipo de problema que normalmente sólo aparecen en casos de esquina. Así que los desarrolladores tienden a salirse con la mala conducta.

También lo que si hago que desee búsquedas de diccionario, etc que se basa en la igualdad de referencia no sobrescritos mis iguales?

Mientras se implementa una interfaz como IEquatable<T> esto no debería ser un problema. La mayoría de las implementaciones de diccionario elegirán un comparador de igualdad de una manera que va a utilizar más de IEqualityComparer<T> Object.ReferenceEquals. Incluso sin EqualityComparer<T>.Default, la mayoría de ellos por defecto a llamar Object.equals () que a su vez entra en tu aplicación.

Básicamente, en la mayor parte del código que se ejecuta quiero la igualdad de referencia y siempre uso == y no estoy anulando eso.

Si usted espera que sus objetos se comporten con la igualdad de valor debe reemplazar == y! = Para hacer cumplir la igualdad de valor para todas las comparaciones. Los usuarios pueden seguir utilizando Object.ReferenceEquals si realmente quieren la igualdad de referencia.

Yo solía pensar que el marco siempre utiliza == y no es igual a comparar cosas

Lo que los usos BCL ha cambiado un poco con el tiempo. Ahora la mayoría de los casos que utilizan la igualdad se tomarán un <=> instancia y utilizarlo para la igualdad. En los casos en los que uno no está especificado que utilizarán <=> para encontrar uno. En el peor caso, esto será por defecto a llamar Object.equals

Otros consejos

Si usted tiene un objeto mutable, no hay mucho punto en reemplazar el método GetHashCode, como realmente no se puede utilizar.Es utilizado por ejemplo por el Dictionary y HashSet colecciones para colocar cada elemento en un cubo.Si cambia el objeto mientras se utiliza como una clave de la colección, el código hash ya no coincide con el cubo que el objeto es, por lo que la colección no funciona correctamente y usted nunca puede encontrar de nuevo el objeto.

Si desea que la búsqueda no utilizar el GetHashCode o Equals método de la clase, siempre puede proporcionar su propio IEqualityComparer la aplicación para usar en su lugar cuando se crea el Dictionary.

El Equals el método está diseñado para la igualdad de valor, por lo que no está mal para implementar esa manera.

Vaya, es realmente varias preguntas en una :-). Así, una tras otra:

  

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

Este consejo común es para el caso en el que desea utilizar el objeto como una clave en un diccionario HashTable / etc. Hashtables por lo general requieren el hash no cambiar, porque lo utilizan para decidir cómo almacenar y recuperar la clave. Si los cambios de hash, la tabla hash es probable que ya no encontrar su objeto.

Para citar los documentos de Java de Mapa Interfaz:

Nota: el gran cuidado debe tener cuidado si se utilizan los objetos mutables como teclas de mapas. 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 un elemento clave en el mapa.

En general es una mala idea utilizar cualquier tipo de objeto mutable como una llave en una tabla hash: Es ni siquiera está claro qué debe ocurrir si una tecla cambia después de que se ha agregado a la tabla hash . En caso de que la tabla hash almacenado devolver el objeto a través de la tecla de edad, o por medio de la nueva clave, o por medio de ambos?

Así que el consejo real es: Utilice sólo los objetos inmutables como claves, y asegurarse de que su código hash nunca cambia, ya sea (que suele ser automática si el objeto es inmutable)

.
  

Además, ¿y si sí quiero búsquedas de diccionario, etc que se basa en la igualdad de referencia no sobrescritos mis iguales?

Bueno, encontrar una aplicación de diccionario que funciona de esa manera. Sin embargo, los diccionarios de biblioteca estándar utilizan el código hash y los iguales, y no hay manera de cambiar eso.

  

Estoy principalmente primordial es igual a la facilidad de la unidad de prueba mi código de serialización que supongo serializar y deserializar (a XML en mi caso) mata a la igualdad de referencia por lo que quiero para asegurarse de que al menos es correcta por la igualdad de valor. ¿Es esta mala práctica redefinir iguales en este caso?

No, me parece que perfectamente aceptable. Sin embargo, no se debe utilizar objetos tales como llaves en un diccionario / tabla hash, ya que son mutables. Ver más arriba.

El tema subyacente es la mejor manera de identificar de forma exclusiva objetos. Usted menciona la serialización / deserialización que es importante porque la integridad referencial se pierde en el proceso.

La respuesta corta, es que los objetos deben ser identificados de forma única por el más pequeño conjunto de campos inmutables que se pueden utilizar para hacerlo. Estos son los campos que debe utilizar cuando overrideing GetHashCode y Equals.

Para probar que es perfectamente razonable para definir lo que sea afirmaciones que necesita, por lo general éstos no están definidos en el tipo en sí, sino más bien como métodos de utilidad en el banco de pruebas. Tal vez un TestSuite.AssertEquals (MiClase, MiClase)?

Tenga en cuenta que GetHashCode y Equals deben trabajar juntos. GetHashCode debe devolver el mismo valor para dos objetos si son iguales. Iguales deben devolver verdadero si y sólo si dos objetos tienen el mismo código hash. (Tenga en cuenta que es posible que dos objetos pueden no ser iguales, pero puede devolver el mismo código hash). Hay un montón de página web que abordan este tema de frente, sólo Google de distancia.

No sé sobre C #, siendo un novato en relación con él, pero en Java, si se sobrescribe equals (), debe también a anular hashCode () para mantener el contrato entre ellos (y viceversa) .. . y java también tiene la misma captura 22; básicamente, forzando utiliza campos inmutables ... Pero esto es un problema sólo para las clases que se utilizan como una clave hash, y Java ha implementaciones alternativas para todas las colecciones basado en hash ..., que tal vez no tan rápido, pero lo hacen effecitely le permiten utilizar un objeto mutable como una llave ... es sólo (por lo general) frunció el ceño como un "mal diseño".

Y me siento la necesidad de señalar que este problema fundamental es atemporal ... Ha existido desde que Adán era un muchacho.

He trabajado en código FORTRAN que es mayor que yo (soy 36) que se rompe cuando se cambia un nombre de usuario (como cuando una chica se casa, o divorciado ... ;-) Por lo tanto es la ingeniería, La solución adoptada fue: el "método" GetHashCode recuerda el hashCode calculado previamente, vuelve a calcular el hashCode (es decir, un marcador isDirty virtual) y si los KeyFields han cambiado devuelve null. Esto hace que la memoria caché para eliminar el usuario "sucio" (llamando a otro GetPreviousHashCode) y luego el caché devuelve null, haciendo que el usuario vuelva a leer desde la base de datos. Un truco interesante y valioso; aunque yo lo diga yo; -)

Voy disyuntiva mutabilidad (sólo deseable en casos extremos) para O (1) Acceso (deseable en todos los casos). Bienvenido a la ingeniería; la tierra del compromiso informado.

Saludos. Keith.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top