2010-03-25 5 views
5

Tengo una clase compleja en mi proyecto C# en la que quiero poder hacer pruebas de igualdad. No es una clase trivial; contiene una variedad de propiedades escalares así como referencias a otros objetos y colecciones (por ejemplo, IDictionary). Por lo que vale, mi clase está sellada.Reemplazando el método de instancia Object.Equals() en C#; ahora Code Analysis/FxCop warning CA2218: "también debería redefinir GetHashCode". ¿Debo suprimir esto?

Para habilitar una optimización del rendimiento en otro lugar de mi sistema (una optimización que evita costosos viajes redondos de red), necesito poder comparar instancias de estos objetos entre sí para la igualdad – que no sea la referencia incorporada igualdad – y por lo tanto estoy anulando el método de instancia Object.Equals(). Sin embargo, ahora que lo he hecho, Visual Studio Análisis de código de 2008 también conocido como FxCop, que sigo activado por defecto, está levantando la siguiente advertencia:

advertencia: CA2218: Microsoft.Usage: Desde 'MySuperDuperClass' redefine Equals, también debería redefinir GetHashCode.

Creo que entiendo la razón de esta advertencia:Si Voy a utilizar este tipo de objetos como el clave en una colección, el código hash es importante. es decir, see this question. Sin embargo, no voy a utilizar estos objetos como la clave de una colección. Nunca.

sentimiento justificado suprimir la advertencia, miré hacia arriba code CA2218 in the MSDN documentation para obtener el nombre completo de la advertencia así que podría aplicar un atributo SuppressMessage a mi clase de la siguiente manera:

[SuppressMessage("Microsoft.Naming", 
     "CA2218:OverrideGetHashCodeOnOverridingEquals", 
     Justification="This class is not to be used as key in a hashtable.")] 

Sin embargo, al leer más, me di cuenta de lo siguiente:

Cómo arreglar Violaciónes

Para corregir una infracción de esta regla, proporciona una implementación de GetHashCode. Para un par de objetos de del mismo tipo, debe asegurarse de que la implementación devuelva el mismo valor si su implementación de Igual devuelve verdadero para el par.

Cuando para suprimir advertencias

----->No suprimir una advertencia de esta regla . [flecha & el énfasis es mío]

lo tanto, me gustaría saber: Por qué no debería suprimir esta advertencia ya que estaba planeando a? ¿Mi caso no garantiza la supresión? No quiero codificar una implementación de GetHashCode() para este objeto que nunca se llamará, ya que mi objeto nunca será la clave en una colección. Si quisiera ser pedante, en lugar de suprimir, ¿sería más razonable para mí anular GetHashCode() con una implementación que arroje una NotImplementedException?


Actualización: que sólo se veía este tema de nuevo en buen libro Effective C# de Bill Wagner, y se establece en "Punto 10: Entender los peligros de GetHashCode()":

Si' re definir un tipo que no se usará como clave en un contenedor , esto no tendrá importancia. Los tipos que representan controles de ventana, controles de página web o conexiones de base de datos es poco probable que se utilicen como claves en una colección . En esos casos, haga nada. Todos los tipos de referencia tendrán código hash que es correcto, incluso si es muy ineficiente. [...] En la mayoría de los tipos que cree, el mejor enfoque es evitar la existencia de GetHashCode() por completo.

... de ahí surgió originalmente la idea de que no debía preocuparme GetHashCode() siempre.

Respuesta

5

No debe suprimirlo. Mira cómo se implementa el método de igualdad. Estoy seguro de que compara uno o más miembros de la clase para determinar la igualdad. Uno de estos miembros suele ser suficiente para distinguir un objeto de otro y, por lo tanto, puede implementar GetHashCode devolviendo membername.GetHashCode();.

+0

En realidad, compara casi todos los miembros de la clase para determinar la igualdad. La clase contiene suposiciones para una serie de cálculos. Cualquiera de ellos siendo diferente es suficiente para que considere los objetos diferentes. Algunas de las suposiciones son arreglos o colecciones de otros números. Así que GetHashCode sería necesariamente tan complejo si se implementara (correctamente). –

+1

@Chris: No tiene que ser así. Una implementación simple pero a menudo aceptable es para XOR los valores GetHashCode de los miembros. –

+1

Una implementación aún más simple y aceptable (pero no siempre óptima) es devolver el valor de GetHashCode() de un solo campo. Eso es lo que sucede para una estructura. –

5

¿Mi $ 0,10 vale? Implementar GetHashCode.

Por mucho que digas que nunca lo necesitarás, puedes cambiar de opinión o que alguien más tenga otras ideas sobre cómo usar el código. Un GetHashCode en funcionamiento no es difícil de hacer, y garantiza que no habrá ningún problema en el futuro.

+0

Si doy mi clase con * "no la utilizo como clave en una colección" *, ¿sería justo lanzar una * NotImplementedException * para GetHashCode() de mi clase? ¿Cuáles son los problemas con ese enfoque? –

+0

Podría, pero ¿cuál es el beneficio real? Una clase con una limitación especial que arroja excepciones cuando se arroja a un diccionario es una responsabilidad. La implementación de GetHashCode lo hace coincidir con nuestras expectativas razonables. –

5

Tan pronto como se le olvide, u otro desarrollador que no lo sepa use esto, alguien tendrá un error doloroso que rastrear. Recomiendo simplemente implementar GetHashCode correctamente y entonces no tendrás que preocuparte por ello. O simplemente no use Equals para su caso especial de comparación de igualdad.

+0

Pero, ¿qué pasa con la idea en mi último párrafo: implementar GetHashCode() para arrojar una NotImplementedException? Sería más difícil para un desarrollador confundirlo como una clave: explotaría (por diseño). –

+1

Si bien puede aliviar ese error, sería mejor definir la semántica ahora en lugar de más tarde. – ermau

4

Los métodos GetHashCode y Equals trabajan en conjunto para proporcionar una semántica de igualdad basada en el valor para su tipo; debe implementarlos juntos.

Para obtener más información sobre este tema, por favor consulte los siguientes artículos:

Enchufe desvergonzado: Estos artículos fueron escritos por mí.

+0

¿Alguna vez GetTashCode() recibe un llamado de la infraestructura, excluyendo cuando se usa un objeto como clave en una colección? –

+3

No estoy seguro, pero lo importante es recordar que en cualquier momento en el futuro podría ser llamado y dado que la recomendación actual es que implemente el método, sería mejor hacerlo. –

14

Si está reallio-Trulio absosmurfly positivo que nunca vamos a usar la cosa como una clave para una tabla hash entonces su propuesta es razonable. Anular GetHashCode; haz que arroje una excepción.

Tenga en cuenta que las tablas hash se ocultan en lugares poco comunes. Muchos operadores de secuencia LINQ usan implementaciones de tabla hash internamente para acelerar las cosas.Al rechazar la implementación de GetHashCode, también rechazas poder usar tu tipo en una variedad de consultas LINQ. Me gusta construir algoritmos que utilicen la memorización para aumentar la velocidad; los memoizers usualmente usan tablas hash. Por lo tanto, también está rechazando la capacidad de memorizar llamadas a métodos que toman su tipo como parámetro.

Alternativamente, si no quiere ser tan duro: Ignore GetHashCode; haz que siempre devuelva cero. Cumple con los requisitos semánticos de GetHashCode; que dos objetos iguales siempre tienen el mismo código hash. Si alguna vez se utiliza como una clave en un diccionario, el rendimiento será terrible, pero puede enfrentar ese problema cuando surja, lo que usted reclama que nunca sucederá.

Todo lo dicho: vamos. Probablemente haya pasado más tiempo escribiendo la pregunta de lo que llevaría implementarla correctamente. Solo hazlo.

+0

Buena respuesta. Y re: * "Probablemente haya pasado más tiempo escribiendo la pregunta de lo que llevaría implementarla correctamente." * ... tiene razón * en este caso * pero en realidad tengo una * serie * de N objetos que necesitan algo como Equals() para apoyar mi optimización. Intentando evitar N * el trabajo. ;-) –

+4

Y gracias especialmente por señalar que LINQ puede implicar el uso de GetHashCode(). Eso es particularmente esclarecedor. –

Cuestiones relacionadas