Jaka jest różnica między interfejsami IComparable i IEquatable?

97

oba interfejsy wydają się porównywać obiekty pod kątem równości, więc jakie są główne różnice między nimi?

SoftwareGeek
źródło

Odpowiedzi:

189

IEquatable sprawdza, czy dwa obiekty są równe.

IComparable narzuca całkowite uporządkowanie porównywanych obiektów.

Na przykład, IEquatablepowie ci, że 5 nie jest równe 7. IComparablepowie ci, że 5 występuje przed 7.

Greg D.
źródło
20

IEquatable<T> dla równości.

IComparable<T> do zamówienia.

Islam Yahiatene
źródło
10

Oprócz odpowiedzi Grega D.

Możesz zaimplementować IComparablebez implementacji IEquatabledla klasy, w której częściowe porządkowanie ma sens i gdzie zdecydowanie chcesz, aby konsument wywnioskował, że tylko dlatego, że CompareTo()zwraca zero, nie oznacza to, że obiekty są równe (w celach innych niż sortowanie).

Damien_The_Unbeliever
źródło
10
Brzmi to bardziej jak funkcja porównująca dla przypadków specjalnych niż jak obiekt IComparablepoprawnie implementujący . Można wymyślić sensowny przykład gdzie CompareTo(…) == 0ma nie implikuje równość? Ja na pewno nie mogę. W rzeczywistości kontrakt interfejsu (zgodnie z MSDN) wymaga, aby CompareTo(…) == 0implikować równość. Mówiąc wprost, w takim przypadku jak twój użyj specjalnego Comparatorprzedmiotu, nie implementuj IComparable.
Konrad Rudolph
2
@Konrad - wskazałem kilka zastrzeżeń - że typ nie implementuje IEquatable (więc oczywiście twórca nie chce dołączać testu równości) i że wyniki CompareTo są używane do sortowania, a nie do oceny równości. Dochodzisz również do pytań, które równość są istotne (odniesienie, wartość, ignorowanie „arbitralnych” atrybutów - niebieska księga o długości 500 stron może być „równa” czerwonej książce o długości 500 stron dla celów IComparable)
Damien_The_Unbeliever,
4
Twoje ostatnie zdanie jest błędne i to jest szczególny błąd, na który chciałem zwrócić uwagę: IComparablejest tutaj całkowicie niewłaściwy. To, co masz, to bardzo szczególna kolejność, która ma zastosowanie tylko w jednej specjalnej sytuacji. W takich sytuacjach implementacja generała IComparablejest niewłaściwa. Po to IComparertam są. Na przykład nie można porządkować ludzi. Ale można je sortować według wynagrodzenia, rozmiaru buta, liczby piegów lub wagi. Dlatego we IComparerwszystkich tych przypadkach zaimplementowalibyśmy różne .
Konrad Rudolph
2
@Konrad Rudolph: A co z czymś w rodzaju klasy „ScheduledEvent”, która ma „coś” zrobić w określonym czasie? Semantyka tego typu pociągałaby za sobą bardzo silne, naturalne uporządkowanie semantyczne oparte na tym, kiedy akcja miała się odbyć, ale łatwo byłoby, gdyby różne wydarzenia zachodziły w tym samym czasie. Można by wymagać użycia ręcznie określonego IComparer, ale uważam, że posiadanie komparatora wbudowanego w klasę byłoby wygodniejsze.
supercat
4
@supercat Wygoda jest ważna, ale to nie wszystko. Poprawność (podobnie jak spójność logiczna) jest ważniejsza, a statyczny system typów jest ważnym narzędziem do weryfikacji tej logicznej spójności. Naruszając udokumentowaną umowę interfejsów, które implementujesz, podważasz system typów. To nie jest dobry pomysł i nigdy bym go nie polecał. W takich sytuacjach użyj zewnętrznej porównywarki.
Konrad Rudolph
7

Jak podano na stronie MSDN dla IEquatable :

Interfejs IComparable definiuje CompareTometodę, która określa kolejność sortowania wystąpień typu implementującego. Interfejs IEquatable definiuje Equalsmetodę, która określa równość wystąpień typu implementującego.

Equals vs. CompareTo

Will Eddins
źródło
3

IComparable <T> definiuje metodę porównania specyficzną dla typu, której można użyć do porządkowania lub sortowania obiektów.

IEquatable <T> definiuje uogólnioną metodę, której można użyć do określenia równości.


Powiedzmy, że masz klasę 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 };

Aby posortować te obiekty, możesz użyć people.Sort();.

Ale to spowoduje wyjątek.

wprowadź opis obrazu tutaj

Framework nie wie, jak sortować te obiekty. Musisz powiedzieć, jak sortować IComparableinterfejs implementujący .

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);
        }
    }
}

Spowoduje to prawidłowe posortowanie tablicy przy użyciu Sort()metody method.


Obok porównania dwóch obiektów możesz skorzystać z Equals()metody method.

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

To zwróci,false ponieważ Equalsmetoda nie wie, jak porównać dwa obiekty. Dlatego musisz zaimplementować IEquatableinterfejs i powiedzieć frameworkowi, jak wykonać porównanie. Rozszerzając poprzedni przykład, będzie wyglądać tak.

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;
        }
    }
}
Nipuna
źródło
1
Dzięki za to świetne wyjaśnienie. Pytanie: dlaczego IEquatableużywa generycznego, <Person>a IComparablenie?
żywiołowy