The main reason to derive a class from a base class is that the base
class can provide code that you can reuse, so you don't have to write it
yourself.
If you'd derive your comparer from the interface, you'd have to create the
code that gives you a default comparer yourself (of course only if you'd need it, but hey, everyone wants free functionality!)
Class EqualityComparer uses the factory design pattern.
In Factory pattern, we create object without exposing the creation logic to the client and refer to newly created object using a common interface.
The nice thing is that all users of the EqualityComparer only have to call prperty default, and everything is done for them to create the proper object that exposes the interface IEqualtiyComparer
The advantage of this, is that if you need an IEqualityComparer as a parameter in a function, then you don't have to check whether class T
implements IEqualtiy<T>
or not, the Dictionary does that for you.
If you derive from EqualtityComparer<T>
and make sure that the derived class follows the factory design pattern then switching between several equaltiy comparers is easy.
Besides, as with any factory, you only have to change the parameters of the factory to let if produce completely different equality comparers.
of course you could create an equality comparer factory without deriving from EqualtyComparer<T>
, but if you do derive your factory can create one extra type of equality comparers: the default equality comparer, which is the one that uses eiether IEquatable<T>
or Object.Equals. You don't have to write any extra code for this, just derive!
Whether you'll find it useful to derive from EqualtyComparer or not, depends on whether you think the factory design pattern is useful.
As an example, suppose you want to check two dictionaries for equality. One could think of several levels of equality:
- Dictionary X and Y are equal if they are the same object
- X and Y are equal if they have equal keys (using the dictionary key comparer), and if their values are the same object
- X and Y are equal if they have they have equal keys (using the dictionary key comparer), and if their values are equal using the default equality comparer for
TValue
- X and Y are equal if they they have equal keys (using the dictionary key comparer), and equal values using a provided equality comparer for values.
If you derive your dictionary comparer class from EqualityComparer, you already have comparer (1). If the provided TValue comparer is derived from EqualityComparer, there is no real difference between (3) and (4).
So let's derive the factory that can create these four comparers:
class DictionaryComparerFactory<TKey, TValue> :
EqualitiyComparer<Dictionary<TKey, TValue>>
{
// By deriving from EqaulityComparer, you already have comparer (1)
// via property Default
// comparer (4):
// X and Y are equal if equal keys and equal values using provided value comparer
public static IEqualityComparer<Dictionary<TKey, TValue>>
CreateContentComparer(IEqualityComparer<TValue> valueComparer)
{
return new DictionaryComparer<TKey, TValue>(valueComparer);
}
// comparer (3): X and Y equal if equal keys and values default equal
// use (4) by providing the default TValue comparer
public static IEqualityComparer<Dictionary<TKey, TValue>>
CreateDefaultValueComparer(IEqualityComparer<TValue> valueComparer)
{
IEqualityComparer<TValue> defaultValueComparer =
EqualtiyComparer<TValue>.Default;
return new DictionaryComparer<TKey, TValue>(defaultValuecomparer);
}
// comparer (2): X and Y are equal if equal keys and values are same object
// use reference equal for values
public IEqualityComparer<TKey, TValue> CreateReferenceValueComparer()
{
IEqualityComparer<TValue> referenceValueComparer = ...
return new DictionaryComparer<TKey, TValue>(referenceValuecomparer);
}
}
For comparer (2) you can use the reference value comparer as described in stackoverflow IEqualityComparer that uses ReferenceEquals
So now we have four different equality comparers by only providing code for one comparer. The rest is re-used!
This reuse wan't as easy without a factory that creates default comparers
Code for comparer (4): use a provided comparer to check equality for TValue
// constructor
protected DictionaryComparer(IEqualityComparer<TValue> valueComparer) : base()
{ // if no comparer provided, use the default comparer
if (Object.ReferenceEquals(valueComparer, null))
this.valueComparer = EqualityComparer<TValue>.Default;
else
this.valueComparer = valueComparer
}
// comparer for TValue initialized in constructor
protected readonly IEqualityComparer<TValue> valueComparer;
public override bool Equals(Dictionary<TKey, TValue> x, Dictionary<TKey, TValue> y)
{
if (x == null) { return y == null; }
if (y == null) return false;
if (Object.ReferenceEquals(x, y)) return true;
if (x.GetType() != y.GetType()) return false;
// now do equality checks according to (4)
foreach (KeyValuePair<TKey, TValue> xKeyValuePair in x)
{
TValue yValue;
if (y.TryGetValue(xKeyValuePair.Key, out yValue))
{ // y also has x.Key. Are values equal?
if (!this.valueComparer.Equals(xKeyValuePair.Value, yValue))
{ // values are not equal
return false;
}
// else: values equal, continue with next key
}
else
{ // y misses a key that is in x
return false;
}
}
// if here, all key/values equal
return true;
}
Now we can simply compare two dictionaries using different comparers:
var dictionaryX = ...
var dictionaryY = ...
var valueComparer1 = ...
var valueComparer2 = ...
var equalityComparer1 = DictionaryComparer<...>.Default();
var equalityComparer2 = DictionaryComparer<...>..CreateDefaultValueComparer();
var equalityComparer3 = DictionaryComparer<...>.CreatereferenceValueComparer();
var equalityComparer4 = DictionaryComparer<...>
.CreateContentComparer(valueCompaerer1);
var equalityComparer5 = DictionaryComparer<...>
.CreateContentComparer(valueCompaerer2);
So the derivation causes that my equality comparer factories always have a proper Defautlt comparer. Saves me in writing the code myself