What's the difference between IComparable & IEquatable interfaces?
Asked Answered
N

5

116

Both the interfaces seem to compare objects for equality, so what are the major differences between them?

Nesmith answered 9/3, 2010 at 15:21 Comment(0)
W
222

IEquatable tests whether two objects are equal.

IComparable imposes a total ordering on the objects being compared.

For example, IEquatable would tell you that 5 is not equal to 7. IComparable would tell you that 5 comes before 7.

Wayside answered 9/3, 2010 at 15:22 Comment(0)
A
30

IEquatable<T> for equality.

IComparable<T> for ordering.

Auroraauroral answered 9/3, 2010 at 16:15 Comment(0)
H
13

In addition to Greg D's answer:

You might implement IComparable without implementing IEquatable for a class where a partial ordering makes sense, and where you very definitely want the consumer to infer that just because CompareTo() returns zero, this does not imply that the objects are equal (for anything other than sorting purposes).

Husbandry answered 9/3, 2010 at 15:30 Comment(11)
That sounds much more like a special-case comparer than like an object implementing IComparable properly. Can you come up with a meaningful example where CompareTo(…) == 0 does not imply equality? I certainly can’t. In fact, the interface contract (as per MSDN) requires that CompareTo(…) == 0 implies equality. To put it bluntly, in such a case as yours, use a special Comparator object, do not implement IComparable.Westbrooke
@Konrad - I did indicate several caveats - that the type doesn't implement IEquatable (so obviously, the originator doesn't want to include an equality test), and that CompareTo results are used for sorting, not to evaluate equality. You also get into questions of which equality is relevant (reference, value, ignoring "arbitrary" attributes - a blue book of 500 pages in length may be "equal" to a red book of 500 pages in length, for the purposes of IComparable)Husbandry
Your last sentence is wrong, and this is the particular mistake I wanted to point out: IComparable is wholly inappropriate here. What you’ve got is a very particular ordering that only applies in one special situation. For such situations, implementing a general IComparable is wrong. This is what IComparers are there for. For example, people cannot be ordered meaningfully. But they can be ordered according to their salary, their shoe size, the number of their freckles or their weight. Hence, we would implement different IComparers for all these cases.Westbrooke
@Konrad Rudolph: What about something like a "ScheduledEvent" class, which is supposed to do "something" at some particular time? The semantics of the type would imply a very strong natural semantic ordering based upon when the action was supposed to take place, but one could easily have different events occur at the same time. One could require the use of a manually-specified IComparer, but I would aver that having a comparator built into the class would be more convenient.Olva
@Olva Convenience is important, but it isn’t everything. Correctness (as in, logical consistency) is more important and the static type system is an important tool for verifying this logical consistency. By violating the documented contract of interfaces that you implement, you are subverting the type system. This is not a good idea, and I would never recommend it. Use an external comparer for such situations.Westbrooke
@KonradRudolph what do you mean by external comparer?Muscarine
@Muscarine A class implementing the IComparer interface.Westbrooke
This is an old thread but I must concur with @KonradRudolph here, he is absolutely right. The comparison logic in some cases should reside in dedicated, separate IComparer implementations rather than being imposed as the default one. In fact, I believe that as soon as an object becomes a bit complex, it's generally good reason enough not to have it implement IComparable at all and systematically use a separate comparer.Cloyd
@KonradRudolph - I agree that logically CompareTo(...) == 0 should imply equality. Earlier you asked for an example where that might not be the case. How about names that are case-preserving, but case-insensitive for equality? In other words, I can have a name "a" and "A", (case is preserved), and they're considered equal (perhaps by converting both to uppercase, just for the equality check). However, for sorting purposes, we want to treat them case sensitively, so they're sorted logically according to cultural rules. Would that justify equality and comparison yielding different results?Hiramhirasuna
@KonradRudolph - also, your first commented cited the MSDN docs as saying that IComparable was required to imply equality for a return value of zero. Clearly it's now 13 years later since you made that observation, but it's worth noting that today the MS docs on IComparable and IComparable<T> no longer make such an assertion. I don't know if this indicates a softening in MS's thinking on this.Hiramhirasuna
@Hiramhirasuna Your example is excellent, the same is true for Unicode collation, where you would for instance want the strings angstrom, ångström, etc. to compare equal, but when sorting them you would generally sort unadorned Latin letters before their modified variants. I still think this merits using a specific comparer rather than putting these semantics into the IComparable implementation: the latter just seems error-prone. It's interesting that the MSDN wording has been softened in that regard.Westbrooke
T
8

As stated on the MSDN Page for IEquatable:

The IComparable interface defines the CompareTo method, which determines the sort order of instances of the implementing type. The IEquatable interface defines the Equals method, which determines the equality of instances of the implementing type.

Equals vs. CompareTo

Tipton answered 9/3, 2010 at 15:22 Comment(0)
D
6

IComparable <T> defines a type specific comparison method which can be used to order or sort objects.

IEquatable <T> defines a generalized method which can be used to implement for determining equality.


Let's say you have Person class

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

To sort these objects you can use people.Sort();.

But this will thrown an an exception.

enter image description here

Framework doesn't know how to sort these objects. You need to tell how to sort implementing IComparable interface.

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

This will sort the array properly with Sort() method.


Next to compare two objects you can use Equals() method.

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

This will return false because Equals method doesn't know how to compare two objects. Therefore you need to implement IEquatable interface and tell the framework how to do the comparison. Extending on the previous example it'll look like this.

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;
        }
    }
}
Dominion answered 28/4, 2019 at 13:7 Comment(1)
Thanks for this great explanation. Question: why does IEquatable use a generic <Person> and IComparable does not?Menology

© 2022 - 2024 — McMap. All rights reserved.