1603 votes

Pourquoi est-il important de remplacer GetHashCode lorsque la méthode Equals est substituée?

Compte tenu de la classe suivante

public class Foo
{
    public int FooId { get; set; }
    public string FooName { get; set; }

    public override bool Equals(object obj)
    {
        Foo fooItem = obj as Foo;

        return fooItem.FooId == this.FooId;
    }

    public override int GetHashCode()
    {
        // Which is preferred?

        return base.GetHashCode();

        //return this.FooId.GetHashCode();
    }
}

J'ai remplacé la méthode Equals parce que Foo représenter une ligne de la table de Foos. Qui est la méthode préférée pour des raisons impérieuses le GetHashCode?

Pourquoi est-il important de remplacer GetHashCode?

1434voto

Marc Gravell Points 482669

Oui, c'est important si votre élément peut être utilisé comme une clé dans un dictionnaire, ou HashSet<T>, etc - depuis ce est utilisé (en l'absense d'une coutume IEqualityComparer<T>) pour regrouper les éléments dans des seaux. Si le hash-code pour les deux éléments ne correspondent pas, ils peuvent jamais être considérées comme égales (Equals sera tout simplement jamais être appelé).

L' GetHashCode() méthode doit refléter l' Equals logique; les règles sont les suivantes:

  • si deux choses sont égales (Equals(...) == true) alors qu'ils doivent retourner la même valeur pour l' GetHashCode()
  • si l' GetHashCode() est égal, il n'est pas nécessaire pour eux d'être le même; c'est une collision, et Equals sera appelé pour voir si c'est une égalité réelle ou pas.

Dans ce cas, il ressemble à "return FooId;" est adapté GetHashCode() mise en œuvre. Si vous testez plusieurs propriétés, il est courant de les combiner à l'aide d'un code comme ci-dessous, afin de réduire la diagonale des collisions (c'est à dire afin qu' new Foo(3,5) a un autre hash-code new Foo(5,3)):

int hash = 13;
hash = (hash * 7) + field1.GetHashCode();
hash = (hash * 7) + field2.GetHashCode();
...
return hash;

Oh - pour plus de commodité, vous pouvez également envisager de fournir == et != opérateurs lors du remplacement d' Equals et GethashCode.


Une démonstration de ce qui se passe quand vous obtenez cette erreur est ici.

150voto

Albic Points 1677

Il est en fait très difficile à mettre en oeuvre GetHashCode() correctement parce que, en plus des règles Marc déjà mentionné, le code de hachage ne devrait pas changer au cours de la durée de vie d'un objet. Par conséquent, les champs qui sont utilisés pour calculer le code de hachage doit être immuable.

J'ai enfin trouvé une solution à ce problème lorsque je travaillais avec NHibernate. Mon approche est de calculer le code de hachage de l'ID de l'objet. L'ID ne peut être réglé que si le constructeur donc, si vous voulez changer l'ID, ce qui est très peu probable, vous devez créer un nouvel objet qui a une nouvelle ID, et donc, un nouveau code de hachage. Cette approche fonctionne mieux avec Guid parce que vous pouvez fournir un constructeur sans paramètre qui génère aléatoirement un numéro d'identification.

71voto

Trap Points 4042

En substituant est Égal, vous êtes essentiellement en indiquant que vous êtes le seul qui sait mieux comment comparer deux instances d'un type donné, de sorte que vous êtes susceptible d'être le meilleur candidat pour fournir le meilleur code de hachage.

Ceci est un exemple de la façon dont ReSharper écrit un GetHashCode() fonctionne pour vous:

    public override int GetHashCode()
    {
        unchecked
        {
            var result = 0;
            result = (result * 397) ^ m_someVar1;
            result = (result * 397) ^ m_someVar2;
            result = (result * 397) ^ m_someVar3;
            result = (result * 397) ^ m_someVar4;
            return result;
        }
    }

Comme vous pouvez le voir, il essaie juste de deviner un bon code de hachage basée sur tous les champs de la classe, mais puisque vous savez que votre objet de domaine du ou des plages de valeurs vous pouvez toujours offrir un meilleur.

42voto

huha Points 741

S'il vous plaît n'oubliez pas de vérifier le paramètre obj contre null lors du remplacement d' Equals(). Et aussi de comparer le type.

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

    Foo fooItem = obj as Foo;

    return fooItem.FooId == this.FooId;
}

La raison pour cela est: Equals doit retourner false comparaison, null. Voir aussi http://msdn.microsoft.com/en-us/library/bsc2ak47.aspx

41voto

Ludmil Tinkov Points 145

Que diriez -

    public override int GetHashCode()
    {
        return string.Format("{0}_{1}_{2}", prop1, prop2, prop3).GetHashCode();
    }

En supposant que le rendement n'est pas un problème :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X