it-swarm-es.tech

¿Cómo implementas GetHashCode para la estructura con dos cadenas, cuando ambas cadenas son intercambiables?

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?

66
Graviton

MSDN :

Una función hash debe tener las siguientes propiedades:

  • Si dos objetos se comparan como iguales, el método GetHashCode para cada objeto debe devolver el mismo valor. Sin embargo, si dos objetos no se comparan como iguales, los métodos GetHashCode para los dos objetos no tienen que devolver valores diferentes.
  • El método GetHashCode para un objeto debe devolver constantemente el mismo código hash siempre que no haya ninguna modificación en el estado del objeto que determine el valor de retorno del método Equals del objeto. Tenga en cuenta que esto es cierto solo para la ejecución actual de una aplicación y que se puede devolver un código hash diferente si la aplicación se ejecuta de nuevo.
  • Para obtener el mejor rendimiento, una función hash debe generar una distribución aleatoria para todas las entradas.

Teniendo en cuenta la forma correcta es:

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

^ puede ser sustituido con otra operación conmutativa

65
aku

Ver respuesta de Jon Skeet - operaciones binarias como ^ no son buenas, ¡a menudo generarán hash en colisión!

25
Tomáš Kafka
public override int GetHashCode()
{
    unchecked
    {
        return (str1 ?? String.Empty).GetHashCode() +
            (str2 ?? String.Empty).GetHashCode();
    }
}

Usar el operador '+' podría ser mejor que usar '^', porque aunque explícitamente desea que ('AA', 'BB') y ('BB', 'AA') sean lo mismo explícitamente, es posible que no desee ( 'AA', 'AA') y ('BB', 'BB') para ser el mismo (o todos los pares iguales para esa materia).

La regla "lo más rápido posible" no se cumple por completo en esta solución porque en el caso de los nulos esto realiza un 'GetHashCode ()' en la cadena vacía en lugar de devolver inmediatamente una constante conocida, pero incluso sin medir explícitamente estoy dispuesto para arriesgarse a adivinar que la diferencia no sería lo suficientemente grande como para preocuparse a menos que espere muchos nulos.

15
Jerry
  1. Como regla general, una forma sencilla de generar un código hash para una clase es XOR todos los campos de datos que pueden participar en la generación del código hash (teniendo cuidado de verificar si hay un valor nulo como lo señalan otros). Esto también cumple con el requisito (¿artificial?) De que los códigos de 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 iguales, XOR puede no ser una buena opción. Pero si str1 y str2 representan, digamos, nombre y apellido, XOR es probablemente una buena opción.

Aunque 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 valor, que no parece ser El caso aquí. - El uso de propiedades con los configuradores para generar un código hash también está pidiendo problemas.

5
Joe

Una forma simple general es hacer esto:

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

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

4
Daniel Lidström

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 número primo de tamaño suficiente para hacer que la variable del resultado se desborde y mezcle 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.

3
Jani Hyytiäinen
public override int GetHashCode()   
{       
    unchecked      
    {           
        return(str1 != null ? str1.GetHashCode() : 0) ^ (str2 != null ? str2.GetHashCode() : 0);       
    }   
}
3
user11556

Ah sí, como señaló Gary Shutler:

return str1.GetHashCode() + str2.GetHashCode();

Puede desbordarse. Puede intentar hacer un casting largo como Artem sugirió, o podría rodear la declaración con la palabra clave sin marcar:

return unchecked(str1.GetHashCode() + str2.GetHashCode());
2
Grokys

Prueba este:

(((long)str1.GetHashCode()) + ((long)str2.GetHashCode())).GetHashCode()
1
Artem Tikhomirov

Muchas posibilidades P.ej.

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

0
VolkerK

Tal vez algo como str1.GetHashCode () + str2.GetHashCode ()? o (str1.GetHashCode () + str2.GetHashCode ())/2? De esta manera, sería lo mismo sin importar si se intercambian str1 y str2 ....

0
Mike Stone

Clasifíquelos, luego concaténelos:

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

El resultado de GetHashCode se supone que es:

  1. Tan rápido como sea posible.
  2. Tan único como sea posible.

Teniendo eso en cuenta, me gustaría ir con algo como esto:

if (str1 == null)
    if (str2 == null)
        return 0;
    else
       return str2.GetHashCode();
else
    if (str2 == null)
        return str1.GetHashCode();
    else
       return ((ulong)str1.GetHashCode() | ((ulong)str2.GetHashCode() << 32)).GetHashCode();

Edit: Olvidé los nulos. Código fijo.

0
Omer van Kloeten