web-dev-qa-db-fra.com

Quelle est la différence entre IEqualityComparer <T> et IEquatable <T>?

Je veux comprendre les scénarios où IEqualityComparer<T> et IEquatable<T> doit être utilisé. La documentation MSDN pour les deux est très similaire.

138
Tilak

IEqualityComparer<T> est une interface pour un objet qui effectue la comparaison sur deux objets de type T.

IEquatable<T> est pour un objet de type T afin qu'il puisse se comparer à un autre.

104
Justin Niessner

Au moment de décider d'utiliser IEquatable<T> ou IEqualityComparer<T> , on pourrait demander:

Existe-t-il une méthode préférée pour tester l'égalité de deux instances de T, ou existe-t-il plusieurs méthodes également valables?

  • S'il n'y a qu'une seule façon de tester l'égalité de deux instances de T, ou si une méthode parmi plusieurs est préférée, alors IEquatable<T> Serait le bon choix: cette interface est censée être implémentée uniquement par T lui-même, de sorte qu'une instance de T ait une connaissance interne de la façon de se comparer à une autre instance de T.

  • D'un autre côté, s'il existe plusieurs méthodes tout aussi raisonnables de comparer deux T pour l'égalité, IEqualityComparer<T> Semble plus approprié: cette interface n'est pas destinée à être implémentée par T lui-même, mais par d'autres classes "externes". Par conséquent, lorsque vous testez l'égalité de deux instances de T, car T n'a aucune compréhension interne de l'égalité, vous devrez faire un choix explicite d'une instance de IEqualityComparer<T> Qui exécute la test selon vos besoins spécifiques.

Exemple:

Considérons ces deux types (qui sont censés avoir sémantique de valeur ):

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ériterait-il de IEquatable<>, Mais pas l'autre?

En théorie, il n'y a qu'une seule façon judicieuse de comparer deux instances de l'un ou l'autre type: elles sont égales si les propriétés X et Y dans les deux instances sont égales. Selon cette réflexion, les deux types devraient implémenter IEquatable<>, Car il ne semble pas probable qu'il existe d'autres façons significatives de faire un test d'égalité.

Le problème ici est que la comparaison des nombres à virgule flottante pour l'égalité peut ne pas fonctionner comme prévu, en raison d'erreurs d'arrondi minute . Il existe différentes méthodes de comparaison des nombres à virgule flottante pour quasi-égalité, chacune avec des avantages et des compromis spécifiques, et vous voudrez peut-être choisir vous-même la méthode appropriée.

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 à laquelle j'ai lié (ci-dessus) indique explicitement que ce test de quasi-égalité a quelques faiblesses. Comme il s'agit d'une implémentation IEqualityComparer<T>, Vous pouvez simplement l'échanger si elle n'est pas assez bonne pour vos besoins.

60
stakx

Vous avez déjà la définition de base de ce qu'ils sont . En bref, si vous implémentez IEquatable<T> sur la classe T, la méthode Equals sur un objet de type T vous indique si l'objet lui-même (celui qui est testé pour l'égalité) est égal à une autre instance de le même type T. Tandis que, IEqualityComparer<T> sert à tester l'égalité de deux instances de T, généralement en dehors de la portée des instances de T.

Quant à à quoi ils servent peut être déroutant au début. D'après la définition, il doit être clair que par conséquent IEquatable<T> (défini dans la classe T elle-même) devrait être le standard de facto pour représenter l'unicité de ses objets/instances. HashSet<T>, Dictionary<T, U> (considérant que GetHashCode est également remplacé), Contains sur List<T> etc utilisez cela. Implémentation de IEqualityComparer<T> on T n'aide pas les cas généraux mentionnés ci-dessus. Par la suite, la mise en œuvre de IEquatable<T> sur toute autre classe autre que T. Cette:

class MyClass : IEquatable<T>

a rarement un sens.

D'autre part

class T : IEquatable<T>
{
    //override ==, !=, GetHashCode and non generic Equals as well

    public bool Equals(T other)
    {
        //....
    }
}

est de savoir comment cela doit être fait.

IEqualityComparer<T> peut être utile lorsque vous avez besoin d'une validation personnalisée de l'égalité, mais pas en règle générale. Par exemple, dans une classe de Person à un moment donné, vous pourriez avoir besoin de tester l'égalité de deux personnes en fonction de leur âge. Dans ce cas, vous pouvez 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 obj.Age.GetHashCode;
    }
}

Pour les tester, essayez

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>

Certes, cela fonctionne, mais n'a pas l'air bien et défait la logique.

Ce dont vous avez généralement besoin est IEquatable<T>. Idéalement, vous ne pouvez avoir qu'un seul IEquatable<T> tandis que plusieurs IEqualityComparer<T> est possible selon différents critères.

Le IEqualityComparer<T> et IEquatable<T> sont exactement analogues à Comparer<T> et IComparable<T> qui sont utilisés à des fins de comparaison plutôt que de comparaison; un bon fil ici où j'ai écrit la même réponse :)

24
nawfal

IEqualityComparer est à utiliser lorsque l'égalité de deux objets est implémentée en externe, par ex. si vous vouliez définir un comparateur pour deux types dont vous n'aviez pas la source, ou pour les cas où l'égalité entre deux choses n'a de sens que dans un contexte limité.

IEquatable est pour l'objet lui-même (celui qui est comparé pour l'égalité) à implémenter.

10
Chris Shain

On compare deux Ts. L'autre peut se comparer aux autres Ts. Habituellement, vous n'aurez besoin d'en utiliser qu'un à la fois, pas les deux.

4
Matt Ball