C# Language
Est égal à et GetHashCode
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)renvoietrue.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 quey.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))renvoietrue, alorsx.Equals(z)renvoietrue.Cohérent : Si vous comparez un objet à plusieurs fois, le résultat est toujours le même.
Les invocations successives dex.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)renvoiefalse.
Implémentations de GetHashCode :
Compatible avec
Equals: Si deux objets sont égaux (ce qui signifie queEqualsrenvoie true),GetHashCodedoit renvoyer la même valeur pour chacun d'eux.Large range : Si deux objets ne sont pas égaux (
Equalsdit 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
Equalsrenvoie true uniquement si les références sont identiques.Si l'instance est un type de valeur, alors
Equalsrenvoie true uniquement si le type et la valeur sont identiques.stringest 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
GetHashCoderenvoie un nombre aléatoire), les éléments ne peuvent pas être trouvés dans uneList, unDictionaryou un élément similaire.
- S'ils ne le font pas (par exemple parce que
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.