it-swarm-pt.tech

Como você implementa GetHashCode para estrutura com duas cadeias, quando as duas cadeias são intercambiáveis

Eu tenho uma estrutura em c #:

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

   public string str2
   {
     get;
     set;
   }   
}

A única regra é que UserInfo(str1="AA", str2="BB").Equals(UserInfo(str1="BB", str2="AA"))

Como substituir a função GetHashCode para essa estrutura?

66
Graviton

MSDN :

Uma função de hash deve ter as seguintes propriedades:

  • Se dois objetos forem comparados como iguais, o método GetHashCode para cada objeto deverá retornar o mesmo valor. No entanto, se dois objetos não forem comparados como iguais, os métodos GetHashCode para os dois objetos não precisarão retornar valores diferentes.
  • O método GetHashCode para um objeto deve retornar consistentemente o mesmo código de hash, desde que não haja modificação no estado do objeto que determine o valor de retorno do método Equals do objeto. Observe que isso é válido apenas para a execução atual de um aplicativo e que um código de hash diferente pode ser retornado se o aplicativo for executado novamente.
  • Para o melhor desempenho, uma função de hash deve gerar uma distribuição aleatória para todas as entradas.

Levar isso em consideração da maneira correta é:

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

^ pode ser substituído por outra operação comutativa

66
aku

Veja resposta de Jon Skeet - operações binárias como ^ não são bons, geralmente geram hash em colisão!

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

Usar o operador '+' pode ser melhor do que usar '^', porque, embora você queira explicitamente ('AA', 'BB') e ('BB', 'AA') sejam explicitamente iguais, talvez não queira ( 'AA', 'AA') e ('BB', 'BB') são os mesmos (ou todos os pares iguais para esse assunto).

A regra 'o mais rápido possível' não é totalmente respeitada nesta solução, porque no caso de nulos, ele executa um 'GetHashCode ()' na cadeia vazia, em vez de retornar imediatamente uma constante conhecida, mas mesmo sem medir explicitamente, estou disposto arriscar um palpite de que a diferença não seria grande o suficiente para se preocupar, a menos que você espere muitos nulos.

15
Jerry
  1. Como regra geral, uma maneira simples de gerar um código de hash para uma classe é XOR todos os campos de dados que podem participar na geração do código de hash (cuidado para verificar se há nulo, conforme indicado por Isso também atende ao requisito (artificial?) de que os códigos de hash para UserInfo ("AA", "BB") e UserInfo ("BB", "AA") sejam os mesmos.

  2. Se você pode fazer suposições sobre o uso de sua classe, talvez possa melhorar sua função de hash. Por exemplo, se for comum str1 e str2 serem iguais, XOR pode não ser uma boa escolha. Mas se str1 e str2 representam, digamos, nome e sobrenome, XOR é provavelmente uma boa escolha.

Embora isso claramente não pretenda ser um exemplo do mundo real, vale a pena ressaltar que: - Este é provavelmente um mau exemplo de uso de uma estrutura: uma estrutura normalmente deve ter semântica de valores, o que não parece ser o caso aqui. - Usar propriedades com setters para gerar um código hash também está causando problemas.

5
Joe

Uma maneira simples geral é fazer isso:

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

A menos que você tenha requisitos rígidos de desempenho, é o mais fácil que consigo pensar e frequentemente uso esse método quando preciso de uma chave composta. Ele lida com os casos null e não causa (m) colisões de hash (em geral). Se você espera '/' em suas strings, escolha outro separador que não espera.

4
Daniel Lidström

Indo ao longo das linhas que ReSharper está sugerindo:

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 é um primo de tamanho suficiente para fazer com que a variável de resultado transborde e misture um pouco os bits do hash, fornecendo uma melhor distribuição dos códigos de hash. Caso contrário, não há nada de especial em 397 que o distinga de outros primos da mesma magnitude.

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

Ah sim, como Gary Shutler apontou:

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

Pode transbordar. Você pode tentar transmitir o conteúdo conforme sugerido por Artem, ou colocar a declaração na palavra-chave desmarcada:

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

Experimente este:

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

Muitas possibilidades. Por exemplo.

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

0
VolkerK

Talvez algo como str1.GetHashCode () + str2.GetHashCode ()? ou (str1.GetHashCode () + str2.GetHashCode ())/2? Dessa forma, seria o mesmo, independentemente de str1 e str2 serem trocados ....

0
Mike Stone

Classifique-os e concatene-os:

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

O resultado de GetHashCode deve ser:

  1. O mais rápido possível.
  2. Tão único quanto possível.

Tendo isso em mente, eu usaria algo assim:

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: Esqueceu os nulos. Código fixo.

0
Omer van Kloeten