59 votes

GetHashCode redéfinition d'un objet contenant un tableau générique

J'ai une classe qui contient les deux propriétés suivantes:

     public int Id      { get; private set; }
    public T[] Values  { get; private set; }
 

Je l'ai fait IEquatable<T> et écrasé le object.Equals comme ceci:

     public override bool Equals(object obj)
    {
        return Equals(obj as SimpleTableRow<T>);
    }
    public bool Equals(SimpleTableRow<T> other)
    {
        // Check for null
        if(ReferenceEquals(other, null))
            return false;

        // Check for same reference
        if(ReferenceEquals(this, other))
            return true;

        // Check for same Id and same Values
        return Id == other.Id && Values.SequenceEqual(other.Values);
    }
 

Lorsque vous avez annulé object.Equals I, vous devez également ignorer GetHashCode de cours. Mais quel code dois-je implémenter? Comment créer un hashcode à partir d'un tableau générique? Et comment puis-je le combiner avec l'entier Id ?

     public override int GetHashCode()
    {
        return // What?
    }
 

91voto

Marc Gravell Points 482669

En raison des problèmes soulevés dans ce fil de discussion, je publie une autre réponse indiquant ce qui se passe si vous vous trompez ... principalement parce que vous ne pouvez pas utiliser le GetHashCode() du tableau; le comportement correct est qu'aucun avertissement n'est imprimé lorsque vous l'exécutez ... modifiez les commentaires pour résoudre le problème:

 using System;
using System.Collections.Generic;
using System.Linq;
static class Program
{
    static void Main()
    {
        // first and second are logically equivalent
        SimpleTableRow<int> first = new SimpleTableRow<int>(1, 2, 3, 4, 5, 6),
            second = new SimpleTableRow<int>(1, 2, 3, 4, 5, 6);

        if (first.Equals(second) && first.GetHashCode() != second.GetHashCode())
        { // proven Equals, but GetHashCode() disagrees
            Console.WriteLine("We have a problem");
        }
        HashSet<SimpleTableRow<int>> set = new HashSet<SimpleTableRow<int>>();
        set.Add(first);
        set.Add(second);
        // which confuses anything that uses hash algorithms
        if (set.Count != 1) Console.WriteLine("Yup, very bad indeed");
    }
}
class SimpleTableRow<T> : IEquatable<SimpleTableRow<T>>
{

    public SimpleTableRow(int id, params T[] values) {
        this.Id = id;
        this.Values = values;
    }
    public int Id { get; private set; }
    public T[] Values { get; private set; }

    public override int GetHashCode() // wrong
    {
        return Id.GetHashCode() ^ Values.GetHashCode();
    }
    /*
    public override int GetHashCode() // right
    {
        int hash = Id;
        if (Values != null)
        {
            hash = (hash * 17) + Values.Length;
            foreach (T t in Values)
            {
                hash *= 17;
                if (t != null) hash = hash + t.GetHashCode();
            }
        }
        return hash;
    }
    */
    public override bool Equals(object obj)
    {
        return Equals(obj as SimpleTableRow<T>);
    }
    public bool Equals(SimpleTableRow<T> other)
    {
        // Check for null
        if (ReferenceEquals(other, null))
            return false;

        // Check for same reference
        if (ReferenceEquals(this, other))
            return true;

        // Check for same Id and same Values
        return Id == other.Id && Values.SequenceEqual(other.Values);
    }
}
 

36voto

Dustin Campbell Points 6323

FWIW, il est très dangereux d'utiliser le contenu de l'Valeurs dans votre code de hachage. Vous ne devriez faire cela que si vous pouvez garantir qu'il ne changera jamais. Cependant, comme il est exposé, je n'ai pas cette garantie est possible. Le hashcode d'un objet ne doit jamais changer. Sinon, il perd de sa valeur comme une clé dans une table de hachage ou un Dictionnaire. Envisager le dur-à-trouver le bug de l'utilisation d'un objet, comme une clé dans une table de hachage, c'est hashcode modifications en raison d'une influence extérieure et vous ne pouvez plus trouver dans la table de hachage!

4voto

Puisque le hashCode est un peu une clé pour stocker l’objet (comme dans une hashtable), j’utiliserais juste Id.GetHashCode ()

2voto

Marc Gravell Points 482669

Que diriez-vous de quelque chose comme:

     public override int GetHashCode()
    {
        int hash = Id;
        if (Values != null)
        {
            hash = (hash * 17) + Values.Length;
            foreach (T t in Values)
            {
                hash *= 17;
                if (t != null) hash = hash + t.GetHashCode();
            }
        }
        return hash;
    }
 

Cela devrait être compatible avec SequenceEqual , plutôt que de faire une comparaison de référence sur le tableau.

1voto

John Saunders Points 118808
public override int GetHashCode()    {
        return Id.GetHashCode() ^ Values.GetHashCode();  
}


Il y a plusieurs points intéressants dans les commentaires et d'autres réponses. L'OP doit examiner si les Valeurs seraient utilisés dans le cadre de la "clé" si l'objet ont été utilisés comme un élément clé dans un dictionnaire. Si oui, alors ils devraient faire partie du code de hachage, sinon, non.

D'autre part, je ne suis pas sûr pourquoi, la méthode GetHashCode devrait miroir SequenceEqual. Il est destiné à calculer un index dans une table de hachage, de ne pas être la complète déterminant de l'égalité. Si il y a beaucoup de table de hachage de collisions à l'aide de l'algorithme ci-dessus, et si elles diffèrent dans la séquence de Valeurs, puis un algorithme doit être choisi qui prend de la séquence en compte. Si la séquence n'a pas vraiment d'importance, d'économiser le temps et ne pas le prendre en compte.

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