Quelle est la différence entre les interfaces IComparable et IEquatable?


Réponses:


188

IEquatable teste si deux objets sont égaux.

IComparable impose un ordre total aux objets comparés.

Par exemple, IEquatablevous dirait que 5 n'est pas égal à 7. IComparablevous dirait que 5 vient avant 7.



10

En plus de la réponse de Greg D:

Vous pouvez implémenter IComparablesans implémenter IEquatablepour une classe où un ordre partiel a du sens, et où vous voulez très certainement que le consommateur en déduit que simplement parce que CompareTo()renvoie zéro, cela n'implique pas que les objets sont égaux (pour autre chose que le tri).


10
Cela ressemble beaucoup plus à un comparateur de cas spécial qu'à un objet implémenté IComparablecorrectement. Pouvez-vous proposer un exemple significatif où CompareTo(…) == 0n'implique pas l' égalité? Je ne peux certainement pas. En fait, le contrat d'interface (selon MSDN) exige que cela CompareTo(…) == 0implique l'égalité. Pour le dire franchement, dans un cas comme le vôtre, utilisez un Comparatorobjet spécial , ne l' implémentez pasIComparable .
Konrad Rudolph

2
@Konrad - j'ai indiqué plusieurs mises en garde - que le type n'implémente pas IEquatable (donc évidemment, l'auteur ne veut pas inclure de test d'égalité), et que les résultats de CompareTo sont utilisés pour le tri, pas pour évaluer l'égalité. Vous vous posez également des questions sur la pertinence de l'égalité (référence, valeur, ignorant les attributs «arbitraires» - un livre bleu de 500 pages peut être «égal» à un livre rouge de 500 pages, aux fins d'IComparable)
Damien_The_Unbeliever

4
Votre dernière phrase est fausse, et c’est l’erreur particulière que je voulais signaler: elle IComparableest totalement inappropriée ici. Ce que vous avez, c'est un ordre très particulier qui ne s'applique que dans une situation particulière. Dans de telles situations, la mise en œuvre d'un général IComparableest une erreur. C'est pour ça que IComparerça sert . Par exemple, les gens ne peuvent pas être ordonnés de manière significative. Mais ils peuvent être commandés en fonction de leur salaire, de leur pointure, du nombre de leurs taches de rousseur ou de leur poids. Par conséquent, nous implémenterions des IComparers différents pour tous ces cas.
Konrad Rudolph

2
@Konrad Rudolph: Qu'en est-il de quelque chose comme une classe "ScheduledEvent", qui est censée faire "quelque chose" à un moment donné? La sémantique du type impliquerait un ordre sémantique naturel très fort basé sur le moment où l'action était censée avoir lieu, mais on pourrait facilement avoir des événements différents en même temps. On pourrait nécessiter l'utilisation d'un IComparer spécifié manuellement, mais je dirais qu'il serait plus pratique d'avoir un comparateur intégré dans la classe.
supercat

4
@supercat La commodité est importante, mais ce n'est pas tout. L'exactitude (comme dans la cohérence logique) est plus importante et le système de type statique est un outil important pour vérifier cette cohérence logique. En violant le contrat documenté des interfaces que vous implémentez, vous subvertissez le système de types. Ce n'est pas une bonne idée et je ne le recommanderais jamais. Utilisez un comparateur externe pour de telles situations.
Konrad Rudolph

7

Comme indiqué sur la page MSDN pour IEquatable :

L'interface IComparable définit la CompareTométhode, qui détermine l'ordre de tri des instances du type d'implémentation. L'interface IEquatable définit la Equalsméthode, qui détermine l'égalité des instances du type d'implémentation.

Equals contre. CompareTo


2

IComparable <T> définit une méthode de comparaison spécifique au type qui peut être utilisée pour ordonner ou trier des objets.

IEquatable <T> définit une méthode généralisée qui peut être utilisée pour implémenter pour déterminer l'égalité.


Disons que vous avez la classe Person

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

Person p1 = new Person() { Name = "Person 1", Age = 34 };
Person p2 = new Person() { Name = "Person 2", Age = 31 };
Person p3 = new Person() { Name = "Person 3", Age = 33 };
Person p4 = new Person() { Name = "Person 4", Age = 26 };

List<Person> people = new List<Person> { p1, p2, p3, p4 };

Pour trier ces objets, vous pouvez utiliser people.Sort();.

Mais cela lèvera une exception.

entrez la description de l'image ici

Framework ne sait pas comment trier ces objets. Vous devez dire comment trier l' IComparableinterface d' implémentation .

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

    public int CompareTo(object obj)
    {
        Person otherPerson = obj as Person;
        if (otherPerson == null)
        {
            throw new ArgumentNullException();
        }
        else
        {
            return Age.CompareTo(otherPerson.Age);
        }
    }
}

Cela triera correctement le tableau avec la Sort()méthode.


À côté de comparer deux objets, vous pouvez utiliser la Equals()méthode.

var newPerson = new Person() { Name = "Person 1", Age = 34 };
var newPersonIsPerson1 = newPerson.Equals(p1);

Cela reviendrafalse car la Equalsméthode ne sait pas comment comparer deux objets. Par conséquent, vous devez implémenter l' IEquatableinterface et indiquer au framework comment effectuer la comparaison. En prolongeant l'exemple précédent, cela ressemblera à ceci.

public class Person : IComparable, IEquatable<Person>
{
    //Some code hidden

    public bool Equals(Person other)
    {
        if (Age == other.Age && Name == other.Name)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

1
Merci pour cette excellente explication. Question: pourquoi IEquatableutilise un générique <Person>et IComparablenon?
veuncent le
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.