2009-02-03 5 views
15

A continuación se muestra un ejemplo de implementación de anular Object.equals() para una clase base entidad de la que todas las demás entidades en una aplicación derivan.¿Cuál es la implementación correcta para GetHashCode() para clases de entidad?

Todas las clases de entidad tienen la propiedad Id, que es un int anulable. (Es la clave principal de cualquier mesa de la clase de entidad corresponde a.)

public override bool Equals(object obj) 
     { 
      if (obj == null || GetType() != obj.GetType()) 
       return false; 

      if (base.Equals(obj)) 
       return true; 

      return Id.HasValue && ((EntityBase) obj).Id.HasValue && 
        Id.Value == ((EntityBase) obj).Id.Value; 
     } 

Dada esta aplicación de Iguales(), ¿cómo se implementa correctamente GetHashCode()?

+0

Para el registro, tener una int nullable para una identificación es una idea horrible. Los ID deben ser siempre GUID y definitivamente no pueden contener nulos. –

Respuesta

23

Si se deriva de algo que ya se anula GetHashCode Me implementarlo como:

public override int GetHashCode() 
{ 
    unchecked 
    { 
     int hash = 37; 
     hash = hash * 23 + base.GetHashCode(); 
     hash = hash * 23 + Id.GetHashCode(); 
     return hash; 
    } 
} 

Un valor nulo de Id devolverá 0 para Id.GetHashCode().

Si su clase sólo se deriva del objeto, que acababa de regresar Id.GetHashCode() - haces no desee incluir la implementación object.GetHashCode en su código hash, ya que en el fondo termina siendo la identidad del objeto.

Tenga en cuenta que la definición de la igualdad voluntad no retorno true si ninguna entidad tiene una identificación, pero el mismo código hash se volvió de ambos objetos. Es posible que desee considerar cambiar su implementación de Equals.

+9

Para aquellos que se preguntan, como yo lo hice: el 23 y el 37 son números arbitrarios que son coprimarios. Jon dijo esto en una respuesta similar aquí: http://www.eggheadcafe.com/software/aspnet/29483139/override-gethashcode.aspx –

+0

@JonSkeet ¿No estaría de acuerdo que agregar 'base.GetHashCode()' usaría Object .GetHashCode, que usa la dirección de memoria. En ese caso, GetHashCode no tiene la propiedad de que cuando Equals devuelve verdadero para dos objetos, sus hashCodes deberían ser los mismos. – Jaap

+0

@JonSkeet También agregaría 'unchecked {}' a las implementaciones de código hash para evitar las opciones del compilador ... Es un caso extremo con el que probablemente nunca se tope ... De cualquier forma, la palabra clave no verificada es una forma de documentar eso los desbordamientos de enteros están bien en la función. – Jaap

1

Solo puede implementar GetHashCode() correctamente si la propiedad Id es inmutable durante el tiempo de vida de una instancia (o al menos durante el tiempo que necesita su hash, como cuando el objeto está en un mapa u otra colección que requiere el hash).

Suponiendo que lo sea, puede simplemente usar el valor de Id como hash para todos los valores válidos y luego usar un hash fijo para null. No puedo recordar lo que el más adecuado es para esto, pero yo asumiría un valor seleccionado al azar para null (seleccionado al azar antes de la compilación, no en tiempo de ejecución) o el valor de la mediana de Id valores válidos (es decir, a medio camino entre 0 y int. Max).

+1

No tiene que ser inmutable durante toda la vida - sólo desde el punto en el que se inserta en primer lugar un mapa o lo que sea.Está bien crear una instancia, desordenarla, ponerla en un mapa y * luego * dejar de jugar con ella. –

+0

Ese es un punto justo. Haré una edición. –

2

Qué respondió Jon Skeet es una buena solución, sin embargo, es posible que desee agregar un bloque de código sin control para permitir entero desbordante

unchecked 
{ 
    int hash = ...; 
    return hash 
} 

https://msdn.microsoft.com/en-us/library/khy08726(v=vs.140).aspx

Si ni controlados ni se especifica sin control, el contexto predeterminado depende de factores externos como las opciones del compilador.

También me gustaría añadir, una vez más, que el uso de base.GetHashCode() el POCO del llamará al defecto object.GetHashCode. Eso definitivamente no es lo que quiere ...

+0

'unchecked' es el valor predeterminado y no es necesario. –

+0

"Si no está marcado ni desmarcado, el contexto predeterminado depende de factores externos como las opciones del compilador". https://msdn.microsoft.com/en-us/library/khy08726(v=vs.140).aspx Así que si escribe ese código, lo pondría allí. También es una forma de documentación. – Jaap

+1

He añadido la parte no seleccionada a mi propia respuesta ahora, por cierto. –

2

Qué acerca del uso del tipo como parte del código hash?
¿Sería ésta una buena puesta en práctica?

public class Foo 
{ 
    public int Id { get; set; } 

    // other properties here 
    // ...... 

    public override int GetHashCode() 
    { 
     int hash = 37; 
     hash = hash * 23 + typeof(Foo).GetHashCode(); 
     hash = hash * 23 + Id.GetHashCode(); 
     return hash; 
    } 
} 
Cuestiones relacionadas