How are equality comparisons performed?
Whenever the BCL classes want to perform an equality check between objects of some type T, they do so by calling one or both of the methods in some implementation of IEqualityComparer<T>. To get hold of such an implementation, the framework looks to EqualityComparer<T>.Default.
As mentioned in the documentation, this property produces an IEqualityComparer<T> like this:
The Default property checks whether type T implements the
System.IEquatable<T> interface and, if so, returns an
EqualityComparer<T> that uses that implementation. Otherwise, it
returns an EqualityComparer<T> that uses the overrides of
Object.Equals and Object.GetHashCode provided by T.
What are my options?
So, in general, to dictate how equality comparisons should be performed you can:
- Explicitly provide an implementation of
IEqualityComparer<T> to the class or method that performs equality checks. This option is not very visible with List<T>, but many LINQ methods (such as Contains) do support it.
- Make your class implement
IEquatable<T>. This will make EqualityComparer<T>.Default use this implementation, and is a good choice whenever there is an obvious "natural" way to compare objects of type T.
- Override
object.GetHashCode and object.Equals without implementing IEqualityComparer<T>. However, this is simply an inferior version of #2 and AFAIK should always be avoided.
Which option to pick?
A good rule of thumb is: if there is an obvious and natural way to compare objects of class T, consider having it implement IEquatable<T>; this will make sure your comparison logic is used throughout the framework without any additional involvement. If there is no obvious candidate, or if you want to compare in a manner different than the default, implement your own IEqualityComparer<T> and pass the implementation as an argument to the class or method that needs to perform equality checks.