[c#] ¿Cómo se implementa GetHashCode para la estructura con dos cadenas, cuando ambas cadenas son intercambiables?



6 Answers

Vea la respuesta de Jon Skeet : las operaciones binarias como ^ no son buenas, ¡a menudo generarán hash colisionante!

Question

Tengo una estructura en C #:

public struct UserInfo
{
   public string str1
   {
     get;
     set;
   }

   public string str2
   {
     get;
     set;
   }   
}

La única regla es que UserInfo(str1="AA", str2="BB").Equals(UserInfo(str1="BB", str2="AA"))

¿Cómo anular la función GetHashCode para esta estructura?




  1. Como regla general, una forma simple de generar un código de hash para una clase es XOR todos los campos de datos que pueden participar en la generación del código de hash (teniendo cuidado de comprobar nulo como lo señalaron otros). Esto también cumple con el requisito (¿artificial?) De que los códigos hash para UserInfo ("AA", "BB") y UserInfo ("BB", "AA") son los mismos.

  2. Si puede hacer suposiciones sobre el uso de su clase, quizás pueda mejorar su función hash. Por ejemplo, si es común que str1 y str2 sean lo mismo, XOR puede no ser una buena opción. Pero si str1 y str2 representan, por ejemplo, nombre y apellido, XOR es probablemente una buena opción.

Aunque esto claramente no pretende ser un ejemplo del mundo real, puede valer la pena señalar que: - Este es probablemente un mal ejemplo de uso de una estructura: una estructura normalmente debería tener una semántica de valores, que no parece ser el caso aquí. - El uso de propiedades con setters para generar un código hash también está pidiendo problemas.




Siguiendo las líneas, ReSharper sugiere:

public int GetHashCode()
{
    unchecked
    {
        int hashCode;

        // String properties
        hashCode = (hashCode * 397) ^ (str1!= null ? str1.GetHashCode() : 0);
        hashCode = (hashCode * 397) ^ (str2!= null ? str1.GetHashCode() : 0);

        // int properties
        hashCode = (hashCode * 397) ^ intProperty;
        return hashCode;
    }
}

397 es un primo de tamaño suficiente para provocar que la variable de resultado se desborde y mezcle un poco los bits del hash, proporcionando una mejor distribución de los códigos hash. De lo contrario, no hay nada especial en 397 que lo distinga de otros números primos de la misma magnitud.




Demasiado complicado, y olvida nulos, etc. Esto se usa para cosas como el cubo, por lo que puede salirse con la suya

if (null != str1) {
    return str1.GetHashCode();
}
if (null != str2) {
    return str2.GetHashCode();
}
//Not sure what you would put here, some constant value will do
return 0;

Esto está sesgado al suponer que str1 no es probable que sea común en una proporción inusualmente grande de instancias.




Una forma general simple es hacer esto:

return string.Format("{0}/{1}", str1, str2).GetHashCode();

A menos que tenga estrictos requisitos de rendimiento, esto es lo más fácil que puedo pensar y con frecuencia uso este método cuando necesito una clave compuesta. Maneja bien los casos null y no provocará (m) colisiones hash (en general). Si espera '/' en sus cadenas, simplemente elija otro separador que no espera.




Muchas posibilidades P.ej

return str1.GetHashCode() ^ str1.GetHashCode()




Ordenarlos, luego concatenarlos:

return ((str1.CompareTo(str2) < 1) ? str1 + str2 : str2 + str1)
    .GetHashCode();





Related



Tags

c# c#   hashtable