From the IEqualityComparer<T>
remarks section on MSDN:
We recommend that you derive from the EqualityComparer<T> class instead of implementing the IEqualityComparer<T> interface, because the EqualityComparer<T> class tests for equality using the IEquatable<T>.Equals method instead of the Object.Equals method. ...
I don't understand the quote's argument of why we would should prefer to derive from EqualityComparer<T>
class instead of implementing IEqualityComparer<T>
. It implies that objects implementing IEqualityComparer<T>
will test for equality using Object.Equals
, but isn't the whole point of implementing IEqualityComparer<T>
when we don't want to test for equality using Object.Equals
or IEquatable<T>.Equals
?
It also implies that if we derive from EqualityComparer<T>
, then derived class will test for equality using IEquatable<T>.Equals
method. Again, isn't the whole point of deriving from EqualityComparer<T>
when we don't want to test for equality using Object.Equals
or IEquatable<T>.Equals
(since EqualityComparer<T>.Default
already test using Object.Equals
or IEquatable<T>.Equals
)?
... This is consistent with the Contains, IndexOf, LastIndexOf, and Remove methods of the Dictionary<TKey, TValue> class and other generic collections.
I assume most collections in the .NET library test for default equality of elements (i.e. when users don't provide their own custom IEqualityComparer<T>
objects to these collections) by calling IEquatable<T>.Equals
or Object.Equals
(depending on whether or not elements of type T
implement IEquatable<T>
) via EqualityComparer<T>.Default
.
Why don't these collections (when testing for default equality) call IEquatable<T>.Equals
or Object.Equals
directly instead of via EqualityComparer<T>.Default
class?
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 theIComparer<T>
interface, because theComparer<T>
class provides an explicit interface implementation of theIComparer.Compare
method and theDefault
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.
I don't understand the suggestion for 1. It seems distinctly odd to me.
As for 2 - very often, you end up with a type (such as Dictionary
) which has an IEqualityComparer<T>
. While the implementation could store a null value and explicitly call Equals
itself, it would be a pain to do so - and would also involve significant ugliness to make sure that it didn't box value types implementing IEquatable<T>
unnecessarily. Using the interface an EqualityComparer<T>.Default
is significantly simpler and more consistent.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With