c# - Anulación de GetHashCode()
resharper (3)
En este artículo , Jon Skeet mencionó que usualmente utiliza este tipo de algoritmo para anular GetHashCode () .
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
int hash = 17;
// Suitable nullity checks etc, of course :)
hash = hash * 23 + Id.GetHashCode();
return hash;
}
}
Ahora, he intentado usar esto, pero Resharper me dice que el método GetHashCode () debería ser hash utilizando solo campos de solo lectura (aunque compila bien). ¿Cuál sería una buena práctica, porque ahora mismo no puedo tener mis campos como de solo lectura?
Intenté generar este método por Resharper, aquí está el resultado.
public override int GetHashCode()
{
return base.GetHashCode();
}
Esto no contribuye mucho, para ser honesto ...
Si todos sus campos son mutables y tiene que implementar el método GetHashCode
, me temo que esta es la implementación que debería tener.
public override int GetHashCode()
{
return 1;
}
Sí, esto es ineficiente, pero esto es al menos correcto.
El problema es que GetHashCode
está siendo utilizado por las colecciones Dictionary y HashSet para colocar cada elemento en un depósito. Si hashcode se calcula en base a algunos campos mutables y los campos realmente se cambian después de que el objeto se coloca en el HashSet o el diccionario, el objeto ya no se puede encontrar desde HashSet o Dictionary.
Tenga en cuenta que con todos los objetos que devuelven el mismo HashCode 1, esto básicamente significa que todos los objetos se colocan en el mismo depósito en el HashSet o el Dictionary. Por lo tanto, siempre hay un solo contenedor en el HashSet o Dictionary. Al intentar buscar el objeto, se realizará una comprobación de igualdad en cada uno de los objetos dentro del único contenedor. Esto es como hacer una búsqueda en una lista vinculada.
Alguien puede argumentar que implementar el código hash basado en campos mutables puede estar bien si podemos asegurarnos de que los campos nunca se cambien después de los objetos agregados a la colección HashCode o Dictionary. Mi opinión personal es que esto es propenso a errores. Alguien que tome el control de tu código dos años después puede no ser consciente de esto y romper el código accidentalmente.
Tenga en cuenta que su GetHashCode debe ir de la mano con su método Equals. Y si solo puede usar la igualdad de referencia (cuando nunca tendría dos instancias diferentes de su clase que puedan ser iguales), entonces puede usar Equals y GetHashCode de manera segura que se heredan de Object. Esto funcionaría mucho mejor que simplemente return 1
desde GetHashCode.
Personalmente, tiendo a devolver un valor numérico diferente para cada implementación de GetHashCode()
en una clase que no tiene campos inmutables. Esto significa que si tengo un diccionario que contiene diferentes tipos de implementación, existe la posibilidad de que las diferentes instancias de diferentes tipos se pongan en diferentes segmentos.
Por ejemplo
public class A
{
// TODO Equals override
public override int GetHashCode()
{
return 21313;
}
}
public class B
{
// TODO Equals override
public override int GetHashCode()
{
return 35507;
}
}
Entonces, si tengo un Dictionary<object, TValue>
contiene instancias de A
, B
y otros tipos, el rendimiento de la búsqueda será mejor que si todas las implementaciones de GetHashCode
devolvieran el mismo valor numérico.
También debe tenerse en cuenta que hago uso de números primos para obtener una mejor distribución.
Según los comentarios, proporcioné una muestra de LINQPad aquí que demuestra la diferencia de rendimiento entre el uso de la return 1
para diferentes tipos y la devolución de un valor diferente para cada tipo.