Основная причина создания класса из базового класса заключается в том, что базовый класс может предоставлять код, который вы можете использовать повторно, поэтому вам не нужно писать его самостоятельно.
Если вы извлекаете ваш компаратор из интерфейса, вам нужно будет создать код, который даст вам сам компаратор по умолчанию (конечно, только если вам это нужно, но, эй, всем нужна бесплатная функциональность!)
Класс EqualityComparer использует фабричный шаблон проектирования .
В шаблоне Factory мы создаем объект без предоставления клиенту логики создания и обращаемся к вновь созданному объекту с помощью общего интерфейса.
Приятно, что всепользователям EqualityComparer нужно только вызвать prperty default, и все сделано для того, чтобы они создали надлежащий объект, который предоставляет интерфейс IEqualtiyComparer
Преимущество этого состоит в том, что если вам нуженIEqualityComparer в качестве параметра в функции, тогда вам не нужно проверять, реализует ли класс T
IEqualtiy<T>
или нет, словарь сделает это за вас.
Если вы наследуете от EqualtityComparer<T>
и убедитесь, что производный класс следует фабричному шаблону проектирования , тогда переключение между несколькими равными компараторами легко.
Кроме того, как и для любой фабрики, вам нужно всего лишь изменить параметры фабрики, чтобы разрешить, если производятся совершенно разные компараторы равенства.
Конечно, вы можете создать фабрику компаратора равенства, не производя отEqualtyComparer<T>
, но если вы производите производную, ваша фабрика может создать один дополнительный тип средств сравнения: средство сравнения по умолчанию, которое использует eiether IEquatable<T>
или Object.Equals.Вам не нужно писать никакого дополнительного кода для этого, просто выведите!
Будет ли полезным выводить из EqualtyComparer или нет, зависит от того, считаете ли вы, что шаблон проектирования фабрики полезен.
В качестве примера предположим, что вы хотите проверить два словаря на равенство.Можно подумать о нескольких уровнях равенства:
- Словарь X и Y равны, если они являются одним и тем же объектом
- X и Y равны, если они имеют равные ключи (используя словарьключ сравнения), и если их значения являются одним и тем же объектом
- X и Y равны, если они имеют одинаковые ключи (с помощью словаря сравнения ключей), и если их значения равны, используя для сравнения равенство по умолчанию для
TValue
- X и Y равны, если они имеют равные ключи (с помощью словаря сравнения ключей), и равные значения, используя предоставленный сравнитель равенства для значений.
Если выПолучите ваш класс сравнения словаря от EqualityComparer, у вас уже есть Comparer (1).Если предоставленный TValue Comparer является производным от EqualityComparer, между (3) и (4) нет реальной разницы.
Итак, давайте выведем фабрику, которая может создать эти четыре средства сравнения:
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);
}
}
Для comparer (2) вы можете использовать эталонный сравнитель значений, как описано в stackoverflow IEqualityComparer, который использует ReferenceEquals
Так что теперь у нас есть четыре разных сравнителя равенства, предоставляя код только для одного сравнителя.Остальное используется повторно!
Это повторное использование не будет таким легким без фабрики, которая создает компараторы по умолчанию
Код для компаратора (4): используйте предоставленный компаратор для проверки равенства для 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;
}
Теперь мы можем просто сравнить два словаря, используя разные компараторы:
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);
Таким образом, вывод приводит к тому, что на моих фабриках компараторов равенства всегда есть надлежащий компаратор Defautlt.Спасает меня в написании кода сам