Equals vs IEqualityComparer, IEquatable<T>, IComparable, IComparer
September 27, 2009
This is a look at the difference between Equals vs IEqualityComparer, IEquatable
Cheatsheet
The TLDR version:
Distinct()
- overrideEquals()
andGetHashcode()
in your object. If T is an interface, then pass aIEqualityComparer<T>
implementation.Dictionary<T>
- your T should overrideEquals()
andGetHashcode()
. If T is an interface, then pass aIEqualityComparer<T>
implementation.- Contains and IndexOf also follow the rules above.
Equals
Besides object comparison, Equals is also used in a few common collection methods. IndexOf(), Contains() both use the Equals() method for equality comparisons. This extends even further with .NET 3.5 and the inclusion of LINQ into the equation, where Equals is used frequently for comparion of methods like Distinct.
IEquatable
This interface was added in .NET 2.0. Its primary use is with value types to avoid the use of ValueType.Equals() (which all structs automatically use if Equals isn’t overriden). ValueType.Equals uses reflection to iterate through every field in the struct, checking for equality. If they are all equal to the object passed then true is returned (see the source further on in the article). Implementing IEquatable
IEqualityComparer
This interface, also added in 2.0, allows you to pass custom object equality checking to a Hashtable, Dictionary and NameValueCollection. You pass in your custom comparison implementation much like you do with IComparer and the Hashtable will use this for equality checks.
The main use of this interface is you can write a single implementation of IEqualityComparer<T>
which several classes can then use, removing the need to rewrite the comparison logic for every class. For example if you class implements IEntity
which has an Id
property, you could then use a comparison class for all equality checks that implements IEqualityComparer<IEntity>
.
IComparer
This is used for custom sorting of objects. Prior to .NET 3.5 it was used primarily for Array.Sort but now finds itself used for sorting with LINQ. It has several default implementations including (as the MSDN page says) Comparer, CaseInsensitiveComparer. When implementing the interface, there is one method to fill:
int Compare(Object x,Object y) Returns: x < y -1 x == y 0 x > y 1
IComparer
Using IComparer with C# 3.0 closures
With the introduction of closures in C# 3.0, you can now shorten your code with IComparer
List<ContentItem> list = N2.Context.CurrentPage.Children.ToList<ContentItem>(); | |
list.Sort(delegate(ContentItem x, ContentItem y) | |
{ | |
if (string.IsNullOrEmpty(x.Title)) | |
return 1; | |
else if (string.IsNullOrEmpty(y.Title)) | |
return -1; | |
return x.Title.CompareTo(y.Title); | |
}); |
IComparable
IComparable gives you one method to implement:
int CompareTo(Object obj)
The same return value system applies as IComparer. The difference between this and IComparer is IComparable will be implemented on the class that contains your field values, while IComparable derived classes are used for custom sorting. So for example:
public class User : IComparable | |
{ | |
public int CompareTo(Object obj) | |
{ | |
// comparison code here | |
} | |
} |
The CompareTo method would be the default way of ordering your User class, perhaps comparing by Name. If you then decided that you wanted to sort a list of User objects by another property, say Age, you could write a class that implemented IComparer instead of altering CompareTo inside User. This would perform the custom sorting, for example:
public class UserAgeComparer : IComparer | |
{ | |
public int CompareTo(Object x, Object y) | |
{ | |
User userX = x as User; | |
User userY = y as User; | |
if (userX != null & userY != null) | |
{ | |
// compare the age. | |
} | |
else | |
{ | |
return 0; | |
} | |
} | |
} |
As with IComparer you can specialise the comparison by implementing IComparable
I'm Chris Small, a software engineer working in London. This is my tech blog. Find out more about me via Github, Stackoverflow, Resume