179

I want to understand the scenarios where IEqualityComparer<T> and IEquatable<T> should be used. The MSDN documentation for both looks very similar.

Tilak
  • 30,108
  • 19
  • 83
  • 131
  • 1
    [MSDN sez:](https://msdn.microsoft.com/en-us/library/ms132151(v=vs.110).aspx)"This interface allows the implementation of customized equality comparison **for collections.**" which of course is inferred in the selected answer. MSDN also recommends inheriting `EqualityComparer` instead of implementing the interface "because `EqualityComparer` tests equality using `IEquatable` – radarbob Sep 20 '16 at 18:38
  • ... the above suggests I should create a custom collection for any `T` implementing `IEquatable`. Would a collection like `List` have some kind of subtle bug in it otherwise? – radarbob Sep 20 '16 at 18:45
  • good explanation http://www.anotherchris.net/csharp/icomparable-icomparer-equals-iequatablet-iequalitycomparer/ – Ramil Shavaleev Feb 18 '19 at 10:17
  • 2
    @RamilShavaleev the link is broken – ChessMax May 02 '20 at 14:44

5 Answers5

141

IEqualityComparer<T> is an interface for an object that performs the comparison on two objects of the type T.

IEquatable<T> is for an object of type T so that it can compare itself to another of the same type.

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
Justin Niessner
  • 242,243
  • 40
  • 408
  • 536
77

When deciding whether to use IEquatable<T> or IEqualityComparer<T>, one could ask:

Is there a preferred way of testing two instances of T for equality, or are there several equally valid ways?

  • If there is only one way of testing two instances of T for equality, or if one of several methods is preferred, then IEquatable<T> would be the right choice: This interface is supposed to be implemented only by T itself, so that one instance of T has internal knowledge of how to compare itself to another instance of T.

  • On the other hand, if there are several equally reasonable methods of comparing two Ts for equality, IEqualityComparer<T> would seem more appropriate: This interface is not meant to be implemented by T itself, but by other "external" classes. Therefore, when testing two instances of T for equality, because T has no internal understanding of equality, you will have to make an explicit choice of a IEqualityComparer<T> instance which performs the test according to your specific requirements.

Example:

Let's consider these two types (which are supposed to have value semantics):

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

Why would only one of these types inherit IEquatable<>, but not the other?

In theory, there is only one sensible way of comparing two instances of either type: They are equal if the X and Y properties in both instances are equal. According to this thinking, both types should implement IEquatable<>, because it doesn't seem likely that there are other meaningful ways of doing an equality test.

The issue here is that comparing floating-point numbers for equality might not work as expected, due to minute rounding errors. There are different methods of comparing floating-point numbers for near-equality, each with specific advantages and trade-offs, and you might want to be able to choose yourself which method is appropriate.

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

Note that the page I linked to (above) explicitly states that this test for near-equality has some weaknesses. Since this is a IEqualityComparer<T> implementation, you can simply swap it out if it's not good enough for your purposes.

stakx - no longer contributing
  • 83,039
  • 20
  • 168
  • 268
  • 7
    The suggested comparison method for testing double-point equality is broken, since any legitimate `IEqualityComparer` implementation *must* implement an equivalence relation, which implies that in *all* cases where two objects compare equal to a third, they *must* compare equal to each other. Any class which implements `IEquatable` or `IEqualityComparer` in a fashion contrary to the above is broken. – supercat Mar 19 '13 at 19:59
  • 6
    A better example would be comparisons between strings. It makes sense to have a string comparison method which regards strings as equal only if they contain the same sequence of bytes, but there are other useful comparison methods *which also constitute equivalence relations*, such as case-insensitive comparisons. Note that a `IEqualityComparer` which considers "hello" equal to both "Hello" and "hElLo" must consider "Hello" and "hElLo" equal to each other, but for most comparison methods that wouldn't be a problem. – supercat Mar 19 '13 at 20:03
  • @supercat, thanks for the valuable feedback. It's likely that I won't get around to revising my answer in the next few days, so if you want, please feel free to edit as you see fit. – stakx - no longer contributing Mar 20 '13 at 08:28
  • This is exactly what I needed and what I'm doing. However there is a need to override GetHashCode, in which it will return false once the values are different. How do you handle your GetHashCode? – TPG Nov 02 '17 at 03:27
  • @teapeng: Not sure what specific use case you're talking about. Generally speaking, `GetHashCode` should allow you to quickly determine whether two values differ. The rules are roughly as follows: **(1)** `GetHashCode` must always produce the same hash code for the same value. **(2)** `GetHashCode` should be fast (faster than `Equals`). **(3)** `GetHashCode` doesn't have to be precise (not as precise as `Equals`). This means it may produce the same hash code for different values. The more precise you can make it, the better, but it's probably more important to keep it fast. – stakx - no longer contributing Nov 02 '17 at 11:18
38

You have already got the basic definition of what they are. In short, if you implement IEquatable<T> on class T, the Equals method on an object of type T tells you if the object itself (the one being tested for equality) is equal to another instance of the same type T. Whereas, IEqualityComparer<T> is for testing the equality of any two instances of T, typically outside the scope of the instances of T.

As to what they are for can be confusing at first. From the definition it should be clear that hence IEquatable<T> (defined in the class T itself) should be the de facto standard to represent uniqueness of its objects/instances. HashSet<T>, Dictionary<T, U> (considering GetHashCode is overridden as well), Contains on List<T> etc make use of this. Implementing IEqualityComparer<T> on T doesn't help the above mentioned general cases. Subsequently, there is little value for implementing IEquatable<T> on any other class other than T. This:

class MyClass : IEquatable<T>

rarely makes sense.

On the other hand

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

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

is how it should be done.

IEqualityComparer<T> can be useful when you require a custom validation of equality, but not as a general rule. For instance, in a class of Person at some point you might require to test equality of two people based on their age. In that case you can do:

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

To test them, try

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

Similarly IEqualityComparer<T> on T doesn't make sense.

class Person : IEqualityComparer<Person>

True this works, but doesn't look good to eyes and defeats logic.

Usually what you need is IEquatable<T>. Also ideally you can have only one IEquatable<T> while multiple IEqualityComparer<T> is possible based on different criteria.

The IEqualityComparer<T> and IEquatable<T> are exactly analogous to Comparer<T> and IComparable<T> which are used for comparison purposes rather than equating; a good thread here where I wrote the same answer :)

Community
  • 1
  • 1
nawfal
  • 70,104
  • 56
  • 326
  • 368
  • `public int GetHashCode(Person obj)` should return `obj.GetHashCode()` – hyankov Dec 02 '16 at 18:16
  • @HristoYankov should return `obj.Age.GetHashCode`. will edit. – nawfal Dec 02 '16 at 18:18
  • Please explain why the comparer must make such an assumption about what the Hash of the object it compares? Your comparer does not know how Person computes its Hash, why would you override that? – hyankov Dec 02 '16 at 19:53
  • @HristoYankov *Your comparer does not know how Person computes its Hash* - Sure, that is why we have not directly called `person.GetHashCode` anywhere.... *why would you override that?* - We override because the whole point of `IEqualityComparer` is to have a different comparison implementation according to our rules - the rules we really know well. – nawfal Dec 03 '16 at 11:44
  • In my example, I need to base my comparison off `Age` property, so I call `Age.GetHashCode`. Age is of type `int`, but whatever the type is the hash code contract in .NET is that `different objects can have equal hash but equal objects cant ever have different hashes`. This is a contract we can blindly believe. Sure our custom comparers should adhere to this rule as well. If ever calling `someObject.GetHashCode` breaks things, then it is the problem with implementors of type of `someObject`, it's not our problem. – nawfal Dec 03 '16 at 11:44
  • This answer is AWESOME, dude. I could not found an understandable explanation ANYWHERE. Thanks! – Michael Haddad Sep 15 '17 at 11:37
14

IEqualityComparer is for use when the equality of two objects is externally implemented, e.g. if you wanted to define a comparer for two types that you did not have the source for, or for cases where equality between two things only makes sense in some limited context.

IEquatable is for the object itself (the one being compared for equality) to implement.

Chris Shain
  • 50,833
  • 6
  • 93
  • 125
5

One compares two Ts. The other can compare itself to other Ts. Usually, you'll only need to use one at a time, not both.

Pang
  • 9,564
  • 146
  • 81
  • 122
Matt Ball
  • 354,903
  • 100
  • 647
  • 710