Differencebetween Iequalitycomparer<T> and Iequatable<T>

What is the difference between IEqualityComparerT and IEquatableT?

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.

Differences between IEquatableT, IEqualityComparerT, and overriding .Equals() when using LINQ on a custom object collection?

It doesn't matter whether you override object.Equals and object.GetHashCode, implement IEquatable, or provide an IEqualityComparer. All of them can work, just in slightly different ways.

1) Overriding Equals and GetHashCode from object:

This is the base case, in a sense. It will generally work, assuming you're in a position to edit the type to ensure that the implementation of the two methods are as desired. There's nothing wrong with doing just this in many cases.

2) Implementing IEquatable

The key point here is that you can (and should) implement IEquatable<YourTypeHere>. The key difference between this and #1 is that you have strong typing for the Equals method, rather than just having it use object. This is both better for convenience to the programmer (added type safety) and also means that any value types won't be boxed, so this can improve performance for custom structs. If you do this you should pretty much always do it in addition to #1, not instead of. Having the Equals method here differ in functionality from object.Equals would be...bad. Don't do that.

3) Implementing IEqualityComparer

This is entirely different from the first two. The idea here is that the object isn't getting it's own hash code, or seeing if it's equal to something else. The point of this approach is that an object doesn't know how to properly get it's hash or see if it's equal to something else. Perhaps it's because you don't control the code of the type (i.e. a 3rd party library) and they didn't bother to override the behavior, or perhaps they did override it but you just want your own unique definition of "equality" in this particular context.

In this case you create an entirely separate "comparer" object that takes in two different objects and informs you of whether they are equal or not, or what the hash code of one object is. When using this solution it doesn't matter what the Equals or GetHashCode methods do in the type itself, you won't use it.


Note that all of this is entirely unrelated from the == operator, which is its own beast.

If I implement IEquatableT, will I lose the option to compare by reference?

There are several different things going on here.

The first is that IEquatable<T> is not directly related to the == operator. If you implement IEquatable<T>, but you don't override the == operator, then == will continue to do what it currently does: compare your objects by reference.

IEquatable<T> gives you an Equals(T) method, and that's it. By itself, it doesn't affect Equals(object) (which you also need to implement), or == and !=.

So let's assume that you do overload the == operator, to call our Equals method:

public static bool operator ==(Foo left, Foo right) => Equals(left, right);
public static bool operator !=(Foo left, Foo right) => !Equals(left, right);

This has only changed the == operator between two Foo instances. You can still write:

if ((object)myObject1 == (object)myObject2))

and that will fall back to using object's == method, which compares by reference.

Another way to do this is:

if (ReferenceEquals(myObject1, myObject2))

which just does the same thing.


Also note that it's rare to implement IEquatable<T> for classes: there's really no point. Classes already have an Equals(object) method and a GetHashCode() method which you need to override, and adding an Equals(T) method doesn't give you much.

IEquatable<T> is however useful for structs: they also have an Equals(object) method you need to override, but if you actually call it then you're going to end up boxing, since it accepts object. If you implement IEquatable<T> here then you also get an Equals(T) method, which you can call without boxing anything.


All of that said, I would write your code as it's intended to work in your application, and do any testing-specific stuff in your test project. This means that if your objects should be compared by reference in your code, I wouldn't add anything new to the object itself.

In your test project, you can write your own method to check whether two instances of your object have the same properties (either as a custom bool AreFoosEqual(Foo f1, Foo f2), or as a full-blown IEqualityComparer<Foo> instance). You can then make this do exactly what your tests need, without worrying about breaking your application.

You can also write your test method as a series of assertions, which tells you which property is incorrect, and what the difference is. This can give you richer test output:

public static void AssertFoosEquals(Foo f1, Foo f2)
{
Assert.AreEqual(f1.Foo, f2.Foo, "Foo property mismatch");
Assert.AreEqual(f1.Bar, f2.Bar, "Bar property mismtach");
}

Preferring EqualityComparerT to IEqualityComparerT

Regarding your first question:

The remarks section for the IEqualityComparer<T> class doesn't really seem to be providing a reason for why you should prefer deriving from the abstract class over the interface, it sounds more like a reason why the equality comparer interface exists in the first place. What it says there is practically useless, it's basically describing what the default implementation is doing. If anything, the "reasoning" they've provided here sound more like a guideline of what your comparers could do and is irrelevant to what it actually does.

Looking at the public/protected interface of the EqualityComparer<T> class, there's only one redeeming quality, it implements the non-generic IEqualityComparer interface. I think what they meant to say that they recommend deriving from it because EqualityComparer<T> actually implements the non-generic IEqualityComparer interface that way your class may be used where the non-generic comparer is required.

It does make more sense in the remarks section for IComparer<T>:

We recommend that you derive from the Comparer<T> class instead of implementing the IComparer<T> interface, because the Comparer<T> class provides an explicit interface implementation of the IComparer.Compare method and the Default property that gets the default comparer for the object.

I suspect it was supposed to say something similar for IEqualityComparer<T> but some ideas were mixed up and ended up with an incomplete description.


Regarding your second question:

A primary goal for the collections found in the library was to be as flexible as possible. One way to get that is to allow custom ways of comparing objects within them by providing a IComparer<T> or IEqualityComparer<T> to do the comparisons. It would be much more easier to get an instance of a default comparer when one was not supplied than it is to do the comparisons directly. These comparers in turn could include the logic necessary to call the appropriate comparisons packaged nicely.

e.g., The default comparers can determine whether T implements IEquatable<T> and call IEquatable<T>.Equals on the object or otherwise use Object.Equals. Better encapsulated here in the comparer than it is potentially repeated in the collections code.

Besides, if they wanted to fall back on calling IEquatable<T>.Equals directly, they would have to add a constraint on T that would make this call possible. Doing so makes it less flexible and negates the benefits of providing the comparer in the first place.

Difference between a model object implementing IEqualityComparer and creating a new class for the model that implements IEqualityComparer

You want to implement IEquatable<T> when your type has a single, default equality comparison. If you want to implement multiple custom equality comparisons for the given type, you can have multiple classes implement IEqualityComparer<T> and pass them to various LINQ methods.

For example, you can have a person class which implements IEquatable<Person>:

public class Person : IEquatable<Person>
{
public Person(string name, int age)
{
Name = name;
Age = age;
}

public string Name { get; }
public int Age { get; }

public bool Equals(Person other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(Name, other.Name) && Age == other.Age;
}

public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Person) obj);
}

public override int GetHashCode()
{
unchecked
{
return ((Name?.GetHashCode() ?? 0)*397) ^ Age;
}
}

public static bool operator ==(Person left, Person right)
{
return Equals(left, right);
}

public static bool operator !=(Person left, Person right)
{
return !Equals(left, right);
}
}

But now you want to implement a custom comparer, which only performs an equality check based on the persons name. You can implement a custom IEqualityComparer<Person>:

public class ByNamePersonComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
return x.Name.Equals(y.Name, StringComparison.OrdinalIgnoreCase);
}

public int GetHashCode(Person obj)
{
return obj.GetHashCode();
}
}

What are the roles of IEqualityComparer and IEquatable in the Enumerable.SequenceEqual method

IEquatable is used by the generic collections like Dictionary to determine if two objects are equal. If the object doesn't implement IEquatable, Object.Equals method is used.

Why should you implement IEquatable? It has better performance than Object.Equals because the object doesn't need to be casted.

When to not implement IEquatable? Some developers believe that you should only implement it on sealed classes.

If IEqualityComparer is specified in SequenceEquals, it is the one used to check the equality of two objects instead of Object.Equal and it's IEquatable implementation. The example for using it in SequenceEqual is in here http://msdn.microsoft.com/en-us/library/bb342073%28v=vs.110%29.aspx. Note that the method signature accepts an IEqualityComparer.

Many collections like Dictionary also accepts IEqualityComparer in it's constructor

Answering your question

If you didn't provide an IEqualityComparer to SequenceEquals, it will use EqualityComparer.Default.

Decompiled code:

public static bool SequenceEqual<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second)
{
return Enumerable.SequenceEqual<TSource>(first, second, (IEqualityComparer<TSource>) null);
}

public static bool SequenceEqual<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
{
if (comparer == null)
comparer = (IEqualityComparer<TSource>) EqualityComparer<TSource>.Default;
...

EqualityComparer.Default checks whether type T implements the System.IEquatable interface and, if so, returns an EqualityComparer that uses that implementation. Otherwise, it returns an EqualityComparer that uses the overrides of Object.Equals and Object.GetHashCode provided by T. This is why your Object.Equals is called.

What's the difference between IEquatable and just overriding Object.Equals()?

The main reason is performance. When generics were introduced in .NET 2.0 they were able to add a bunch of neat classes such as List<T>, Dictionary<K,V>, HashSet<T>, etc. These structures make heavy use of GetHashCode and Equals. But for value types this required boxing. IEquatable<T> lets a structure implement a strongly typed Equals method so no boxing is required. Thus much better performance when using value types with generic collections.

Reference types don't benefit as much but the IEquatable<T> implementation does let you avoid a cast from System.Object which can make a difference if it's called frequently.

As noted on Jared Parson's blog though, you must still implement the standard Object.Equals and Object.GetHashcode overrides.

Overriding IEquatableT when T is an interface and hashcodes are different between derived types

Consider making the a IEqualityComparer<> class to compare the common values.

I have renamed the interface to ICommon for readability

public interface ICommon
{
int SomeInt { get; }
bool SomeBool { get; }
float SomeFloat { get; }
}

public class CommonComparer : IEqualityComparer<ICommon>
{
public bool Equals(ICommon x, ICommon y)
{
return x.SomeInt.Equals(y.SomeInt)
&& x.SomeBool.Equals(y.SomeBool)
&& x.SomeFloat.Equals(y.SomeFloat);
}

public int GetHashCode(ICommon obj)
{
unchecked
{
int hc = -1817952719;
hc = (-1521134295)*hc + obj.SomeInt.GetHashCode();
hc = (-1521134295)*hc + obj.SomeBool.GetHashCode();
hc = (-1521134295)*hc + obj.SomeFloat.GetHashCode();
return hc;
}
}
}

and the program can distinguish between the equal items on two lists.

class Program
{
static void Main(string[] args)
{
var listA = new List<A>
{
new A(1001, true, 1.001f, "A1"),
new A(1002, true, 1.002f, "A2"),
new A(1003, false, 1.003f, "A1"),
new A(1004, false, 1.004f, "A4")
};

var listB = new List<B>
{
new B(1001, true, 1.001f, "B1", 2.5),
new B(1002, false, 1.002f, "B2", 2.8),
new B(1003, true, 1.003f, "B3", 2.9),
new B(1004, false, 1.004f, "B4", 2.9)
};

var common = Enumerable.Intersect(listA, listB, new CommonComparer()).OfType<ICommon>();

Console.WriteLine($"{"SomeInt",-8} {"Bool",-6} {"SomeFloat",-10}");
foreach (var item in common)
{
Console.WriteLine($"{item.SomeInt,-8} {item.SomeBool,-6} {item.SomeFloat,-10}");
}
//SomeInt Bool SomeFloat
//1001 True 1.001
//1004 False 1.004

}
}

and the rest of the code definitions

public class A : ICommon, IEquatable<A>
{
static readonly CommonComparer comparer = new CommonComparer();

public int SomeInt { get; }
public bool SomeBool { get; }
public float SomeFloat { get; }

private readonly string _someARelatedStuff;
// Rest of class...
public A(ICommon other, string someARelatedStuff)
: this(other.SomeInt, other.SomeBool, other.SomeFloat, someARelatedStuff)
{ }
public A(int someInt, bool someBool, float someFloat, string someARelatedStuff)
{
this.SomeInt = someInt;
this.SomeBool = someBool;
this.SomeFloat = someFloat;
this._someARelatedStuff = someARelatedStuff;
}

public override string ToString() => _someARelatedStuff;

#region IEquatable Members
public override bool Equals(object obj)
{
if (obj is A other)
{
return Equals(other);
}
return false;
}

public virtual bool Equals(A other)
{
return comparer.Equals(this, other)
&& _someARelatedStuff.Equals(other._someARelatedStuff);
}

public override int GetHashCode()
{
unchecked
{
int hc = comparer.GetHashCode(this);
hc = (-1521134295)*hc + _someARelatedStuff.GetHashCode();
return hc;
}
}

#endregion

}

public class B : ICommon, IEquatable<B>
{
static readonly CommonComparer comparer = new CommonComparer();

public int SomeInt { get; }
public bool SomeBool { get; }
public float SomeFloat { get; }

readonly string _someBRelatedStuff;
readonly double _someOtherBRelatedStuff;
// Rest of class...

public B(ICommon other, string someBRelatedStuff, double someOtherBRelatedStuff)
: this(other.SomeInt, other.SomeBool, other.SomeFloat, someBRelatedStuff, someOtherBRelatedStuff)
{ }
public B(int someInt, bool someBool, float someFloat, string someBRelatedStuff, double someOtherBRelatedStuff)
{
this.SomeInt = someInt;
this.SomeBool = someBool;
this.SomeFloat = someFloat;
this._someBRelatedStuff = someBRelatedStuff;
this._someOtherBRelatedStuff = someOtherBRelatedStuff;
}

public override string ToString() => $"{_someBRelatedStuff}, {_someOtherBRelatedStuff.ToString("g4")}";

#region IEquatable Members

public override bool Equals(object obj)
{
if (obj is B other)
{
return Equals(other);
}
return false;
}

public virtual bool Equals(B other)
{
return comparer.Equals(this, other)
&& _someBRelatedStuff.Equals(other._someBRelatedStuff)
&& _someOtherBRelatedStuff.Equals(other._someOtherBRelatedStuff);
}

public override int GetHashCode()
{
unchecked
{
int hc = comparer.GetHashCode(this);
hc = (-1521134295)*hc + _someBRelatedStuff.GetHashCode();
hc = (-1521134295)*hc + _someOtherBRelatedStuff.GetHashCode();
return hc;
}
}

#endregion
}

IEqualityComparer vs EqualityComparer?

I think the other post you mention is saying that EqualityComparer<Box> implements IEqualityComparer<Box> and IEqualityComparer, so you don't have to implement both the generic and non-generic interfaces if you derive from EqualityComparer<Box>.

Using of IEqualityComparerT interface and EqualityComparerT class in C#

IEqualityComparer<in T> is an interface that will handle equality comparisons for your collection. Your collection will delegate equiality comparisons to this interface. You may ask, why don't just call the Equals method?

Because there can be several kinds of possible comparisons. Let's take an easy example: are "Abc" and "ABC" equal? It depends. "Abc".Equals("ABC") == false but what if you want case insensitivity?

This is why your collection should delegate the equality comparisons to a different class. By composing classes, you'll respect the single responsibility principle: Your collection knows how to store the items, and the equality comparer knows if they're equal.

An example with sets:

var caseSensitive = new HashSet<string>(StringComparer.Ordinal) // The default anyway
{
"Abc", "ABC"
};

var caseInsensitive = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"Abc", "ABC"
};

The results will be:

caseSensitive.Count == 2
caseInsensitive.Count == 1

caseSensitive.Contains("aBc") == false
caseInsensitive.Contains("aBc") == true

Here you have two completely different set semantics using the same HashSet class.

Now, what's in an IEqualityComparer<in T>?

  • bool Equals(T x, T y);: This method does just what you'd expect it to: it returns true if x should be considered equal to y. Just like the mathematical equality, it must be:
    • reflexive: Equals(x, x) == true
    • symmetric: Equals(x, y) == Equals(y, x)
    • transitive: if Equals(x, y) && Equals(y, z) then Equals(x, z)
  • int GetHashCode(T obj); this one may be trickier to get right. For each obj, it should return a hash code wilth the following properties:
    • it should never change
    • if Equals(x, y) then GetHashCode(x) == GetHashCode(y)
    • there should be as few collisions as possible

Note that this does not imply that if GetHashCode(x) == GetHashCode(y) then Equals(x, y). Two objects can have the same hash code but be inequal (there can be at most 0xFFFFFFFF possible hash codes after all).

Collections often use the hash code to organize their items. For example, the HashSet will know that if two objects don't have the same hash code, they won't be equal and thus can organize its buckets accordingly. Hash codes are just an optimization.

Now, what's EqualityComparer<T>.Default? It's a conventient shortcut for an IEqualityComparer<T> that will use an object's own Equals and GetHashCode functions. This is a good default value as this is what you want to do most of the time: while strings can have multiple natural comparison types, this is not the case for integers for instance.

EqualityComparer<T>.Default will handle a couple special cases:

  • if T is IEquatable<T>, it will use the IEquatable<T> interface
  • if T is Nullable<U> and U is IEquatable<U> it will handle the case properly
  • it will optimize for a couple special cases: byte[] and int-basd Enum.


Related Topics



Leave a reply



Submit