Recherche…


Remarques

Chaque implémentation de Equals doit répondre aux exigences suivantes:

  • Réfléchissant : Un objet doit être égal à lui-même.
    x.Equals(x) renvoie true .

  • Symétrique : Il n'y a pas de différence si je compare x à y ou y à x - le résultat est le même.
    x.Equals(y) renvoie la même valeur que y.Equals(x) .

  • Transitif : Si un objet est égal à un autre objet et que celui-ci est égal à un troisième, le premier doit être égal au troisième.
    if (x.Equals(y) && y.Equals(z)) renvoie true , alors x.Equals(z) renvoie true .

  • Cohérent : Si vous comparez un objet à plusieurs fois, le résultat est toujours le même.
    Les invocations successives de x.Equals(y) renvoient la même valeur tant que les objets référencés par x et y ne sont pas modifiés.

  • Comparaison à null : Aucun objet n'est égal à null .
    x.Equals(null) renvoie false .

Implémentations de GetHashCode :

  • Compatible avec Equals : Si deux objets sont égaux (ce qui signifie que Equals renvoie true), GetHashCode doit renvoyer la même valeur pour chacun d'eux.

  • Large range : Si deux objets ne sont pas égaux ( Equals dit false), il devrait y avoir une forte probabilité que leurs codes de hachage soient distincts. Un hachage parfait n'est souvent pas possible car il y a un nombre limité de valeurs à choisir.

  • Bon marché : le calcul du code de hachage dans tous les cas devrait être peu coûteux.

Voir: Directives pour la surcharge d'Equals () et de l'opérateur ==

Comportement par défaut égal.

Equals est déclaré dans la classe Object elle-même.

public virtual bool Equals(Object obj);

Par défaut, Equals a le comportement suivant:

  • Si l'instance est un type de référence, alors Equals renvoie true uniquement si les références sont identiques.

  • Si l'instance est un type de valeur, alors Equals renvoie true uniquement si le type et la valeur sont identiques.

  • string est un cas particulier. Il se comporte comme un type de valeur.

namespace ConsoleApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
            //areFooClassEqual: False
            Foo fooClass1 = new Foo("42");
            Foo fooClass2 = new Foo("42");
            bool areFooClassEqual = fooClass1.Equals(fooClass2);
            Console.WriteLine("fooClass1 and fooClass2 are equal: {0}", areFooClassEqual);
            //False

            //areFooIntEqual: True
            int fooInt1 = 42;
            int fooInt2 = 42;
            bool areFooIntEqual = fooInt1.Equals(fooInt2);
            Console.WriteLine("fooInt1 and fooInt2 are equal: {0}", areFooIntEqual);

            //areFooStringEqual: True
            string fooString1 = "42";
            string fooString2 = "42";
            bool areFooStringEqual = fooString1.Equals(fooString2);
            Console.WriteLine("fooString1 and fooString2 are equal: {0}", areFooStringEqual);
        }
    }

    public class Foo
    {
        public string Bar { get; }

        public Foo(string bar)
        {
            Bar = bar;
        }
    }
}

Écrire un bon remplacement GetHashCode

GetHashCode a des effets de performance majeurs sur Dictionary <> et HashTable.

Bonnes méthodes GetHashCode

  • devrait avoir une distribution uniforme
    • chaque entier devrait avoir une chance à peu près égale de retourner pour une instance aléatoire
    • si votre méthode retourne le même entier (par exemple, la constante '999') pour chaque instance, vous aurez de mauvaises performances
  • devrait être rapide
    • Ce ne sont PAS des hachages cryptographiques, où la lenteur est une caractéristique
    • plus votre fonction de hachage est lente, plus votre dictionnaire est lent
  • doit renvoyer le même HashCode sur deux instances évaluées par Equals à true
    • S'ils ne le font pas (par exemple parce que GetHashCode renvoie un nombre aléatoire), les éléments ne peuvent pas être trouvés dans une List , un Dictionary ou un élément similaire.

Une bonne méthode pour implémenter GetHashCode consiste à utiliser un nombre premier comme valeur de départ et à ajouter les codes de hachage des champs du type multiplié par d'autres nombres premiers:

public override int GetHashCode()
{
    unchecked // Overflow is fine, just wrap
    {
        int hash = 3049; // Start value (prime number).

        // Suitable nullity checks etc, of course :)
        hash = hash * 5039 + field1.GetHashCode();
        hash = hash * 883 + field2.GetHashCode();
        hash = hash * 9719 + field3.GetHashCode();
        return hash;
    }
}

Seuls les champs utilisés dans la méthode Equals doivent être utilisés pour la fonction de hachage.

Si vous avez besoin de traiter le même type de différentes manières pour Dictionary / HashTables, vous pouvez utiliser IEqualityComparer.

Remplacer Equals et GetHashCode sur les types personnalisés

Pour une classe Person comme:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Clothes { get; set; }
}

var person1 = new Person { Name = "Jon", Age = 20, Clothes = "some clothes" };
var person2 = new Person { Name = "Jon", Age = 20, Clothes = "some other clothes" };

bool result = person1.Equals(person2); //false because it's reference Equals

Mais définir Equals et GetHashCode comme suit:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Clothes { get; set; }

    public override bool Equals(object obj)
    {
        var person = obj as Person;
        if(person == null) return false;
        return Name == person.Name && Age == person.Age; //the clothes are not important when comparing two persons
    }

    public override int GetHashCode()
    {
        return Name.GetHashCode()*Age;
    }
}

var person1 = new Person { Name = "Jon", Age = 20, Clothes = "some clothes" };
var person2 = new Person { Name = "Jon", Age = 20, Clothes = "some other clothes" };

bool result = person1.Equals(person2); // result is true

L'utilisation de LINQ pour effectuer des requêtes différentes sur des personnes vérifie également Equals et GetHashCode :

var persons = new List<Person>
{
     new Person{ Name = "Jon", Age = 20, Clothes = "some clothes"},
     new Person{ Name = "Dave", Age = 20, Clothes = "some other clothes"},
     new Person{ Name = "Jon", Age = 20, Clothes = ""}
};

var distinctPersons = persons.Distinct().ToList();//distinctPersons has Count = 2

Est égal à et GetHashCode dans IEqualityComparator

Pour le type donné Person :

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Clothes { get; set; }
}

List<Person> persons = new List<Person>
{
    new Person{ Name = "Jon", Age = 20, Clothes = "some clothes"},
    new Person{ Name = "Dave", Age = 20, Clothes = "some other clothes"},
    new Person{ Name = "Jon", Age = 20, Clothes = ""}
};

var distinctPersons = persons.Distinct().ToList();// distinctPersons has Count = 3

Mais définir Equals et GetHashCode dans un IEqualityComparator :

public class PersonComparator : IEqualityComparer<Person>
{
    public bool Equals(Person x, Person y)
    {
        return x.Name == y.Name && x.Age == y.Age; //the clothes are not important when comparing two persons;
    }

    public int GetHashCode(Person obj) { return obj.Name.GetHashCode() * obj.Age; }
}

var distinctPersons = persons.Distinct(new PersonComparator()).ToList();// distinctPersons has Count = 2

Notez que pour cette requête, deux objets ont été considérés égaux si les deux Equals renvoyés sont true et que GetHashCode a renvoyé le même code de hachage pour les deux personnes.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow