Je veux comprendre les scénarios IEqualityComparer<T>
et IEquatable<T>
doit être utilisé.
La documentation MSDN pour les deux l'air très similaires.
Réponses
Trop de publicités?Lorsque vous décidez d'utiliser IEquatable<T>
ou IEqualityComparer<T>
,
on pourrait se demander:
Est-il un moyen privilégié de test de deux instances d'
T
pour l'égalité, ou il y en a plusieurs aussi valides l'une des façons?
Si il y a un seul moyen de tester les deux instances de l'
T
pour l'égalité, ou si l'une de plusieurs méthodes est préférée,IEquatable<T>
serait le bon choix: Cette interface est censé être mis en œuvre que parT
lui-même, de sorte qu'une instance deT
a la connaissance interne de façon à se comparer à une autre instance d'T
.D'autre part, s'il y a plusieurs également des méthodes de comparaison de deux
T
s pour l'égalité,IEqualityComparer<T>
semble plus approprié: Cette interface n'est pas destiné à être mis en œuvre parT
lui-même, mais par d'autres "externe" des classes. Par conséquent, lors de l'essai de deux instances de l'T
pour l'égalité, parce qu'T
n'a pas de connaissance interne de l'égalité, vous aurez à faire un choix explicite d'unIEqualityComparer<T>
instance qui effectue le test en fonction de vos exigences spécifiques.
Exemple:
Nous allons tenir compte de ces deux types (qui sont censés avoir une valeur sémantique):
interface IIntPoint : IEquatable<IIntPoint>
{
int X { get; }
int Y { get; }
}
interface IDoublePoint // does not inherit IEquatable<IDoublePoint>; see below.
{
double X { get; }
double Y { get; }
}
Pourquoi un seul de ces types héritent IEquatable<>
, mais pas dans l'autre?
En théorie, il y a une seule façon logique de la comparaison de deux instances de chaque type: Ils sont égaux si l' X
et Y
propriétés dans les deux cas, sont égaux. Selon cette pensée, les deux types doivent mettre en œuvre IEquatable<>
,, car il ne semble pas probable qu'il existe d'autres moyens efficaces de faire un test d'égalité.
Le problème ici est que la comparaison de nombres à virgule flottante pour l'égalité peut ne pas fonctionner comme prévu, en raison de minutes à des erreurs d'arrondi. Il existe différentes méthodes de comparaison de nombres à virgule flottante pour la quasi-égalité, chacune avec ses avantages et inconvénients, et vous pourriez être en mesure de choisir vous-même la méthode qui convient.
sealed class DoublePointNearEqualityComparerByTolerance : IEqualityComparer<IDoublePoint>
{
public DoublePointNearEqualityComparerByTolerance(double tolerance) { … }
…
public bool Equals(IDoublePoint a, IDoublePoint b)
{
return Math.Abs(a.X - b.X) <= tolerance && Math.Abs(a.Y - b.Y) <= tolerance;
}
…
}
Notez que la page (ci-dessus) indique explicitement que ce test pour la quasi-égalité a quelques faiblesses. Puisque c'est un IEqualityComparer<T>
mise en œuvre, vous pouvez simplement remplacez-la si elle n'est pas assez bon pour vos besoins.
Vous avez déjà eu la définition de base de ce qu'ils sont. En bref, si vous implémentez IEquatable<T>
classe T
, Equals
méthode sur un objet de type T
vous indique si l'objet lui-même (un cours d'essai pour l'égalité) est égale à une autre instance du même type T
. Alors que, IEqualityComparer<T>
est pour tester l'égalité de deux instances d' T
, généralement en dehors de la portée des instances de T
.
Comme pour ce qu'ils sont peut être déroutant au premier abord. À partir de la définition, il devrait être clair que, partant, IEquatable<T>
(défini dans la classe T
lui-même) doit être la norme de facto pour représenter la singularité de ses objets/instances. HashSet<T>
, Dictionary<T, U>
(en considérant GetHashCode
est remplacé en tant que bien), Contains
sur List<T>
etc faire usage de cette. La mise en œuvre de IEqualityComparer<T>
sur T
n'aide pas le ci-dessus mentionné les cas généraux. Par la suite, il n'y a que peu de valeur pour la mise en oeuvre IEquatable<T>
sur toute autre catégorie autre que T
. Ce:
class MyClass : IEquatable<T>
rarement un sens.
Sur l'autre main
class T : IEquatable<T>
{
//override ==, !=, GetHashCode and non generic Equals as well
public bool Equals(T other)
{
//....
}
}
est comment il devrait être fait.
IEqualityComparer<T>
peut être utile lorsque vous avez besoin d'une validation personnalisée de l'égalité, mais pas comme une règle générale. Par exemple, dans une classe de Person
à un certain point, vous aurez besoin de tester l'égalité de deux personnes sur la base de leur âge. Dans ce cas, vous pouvez le faire:
class Person
{
public int Age;
}
class AgeEqualityTester : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
return x.Age == y.Age;
}
public int GetHashCode(Person obj)
{
return Age.GetHashCode;
}
}
Pour les tester, essayer
var people = new Person[] { new Person { age = 23 } };
Person p = new Person() { age = 23 };
print people.Contains(p); //false;
print people.Contains(p, new AgeEqualityTester()); //true
De même, IEqualityComparer<T>
sur T
n'a pas de sens.
class Person : IEqualityComparer<Person>
Vrai cela fonctionne, mais n'a pas l'air bon pour les yeux et va à l'encontre de la logique.
Généralement ce que vous avez besoin est - IEquatable<T>
. Aussi, idéalement, vous ne pouvez avoir qu'un seul IEquatable<T>
alors que plusieurs IEqualityComparer<T>
est possible sur la base de différents critères.
L' IEqualityComparer<T>
et IEquatable<T>
sont exactement analogue à l' Comparer<T>
et IComparable<T>
qui sont utilisés à des fins de comparaison plutôt que de les assimiler; un bon thread ici où j'ai écrit la même réponse :)
IEqualityComparer est à utiliser lorsque l'égalité de deux objets en usage externe, mise en œuvre, par exemple, si vous vouliez définir un comparateur pour les deux types que vous n'avez pas la source, ou pour les cas où l'égalité entre les deux choses n'a de sens que dans certains contexte.
IEquatable est pour l'objet lui-même (celui d'être comparé pour l'égalité) à mettre en œuvre.