来自MSDN上的IEqualityComparer<T>
备注部分:
我们建议您从中获取 EqualityComparer&LT; T&GT;而不是 实现IEqualityComparer&lt; T&gt; 界面,因为 EqualityComparer&LT; T&GT;班级考试 平等使用 IEquatable&lt; T&gt; .Equals方法代替 Object.Equals方法。 ...
我不明白引用的论点,为什么我们宁愿从EqualityComparer<T>
类派生而不是实现IEqualityComparer<T>
。这意味着实现IEqualityComparer<T>
的对象将使用Object.Equals
测试相等性,但当我们不想使用{{1}测试相等性时,不是实现IEqualityComparer<T>
的全部要点}或Object.Equals
?
它还暗示如果我们从IEquatable<T>.Equals
派生,那么派生类将使用EqualityComparer<T>
方法测试相等性。同样,当我们不想使用IEquatable<T>.Equals
或EqualityComparer<T>
(因为Object.Equals
已使用{{IEquatable<T>.Equals
进行测试时,不是从EqualityComparer<T>.Default
派生的全部内容1}}或Object.Equals
)?
......这与此一致 包含,IndexOf,LastIndexOf和 删除Dictionary&lt; TKey的方法, TValue&GT;类和其他通用的 集合。
我假设.NET库中的大多数集合测试默认元素的相等性(即当用户不向这些集合提供自己的自定义IEquatable<T>.Equals
对象时)通过IEqualityComparer<T>
调用IEquatable<T>.Equals
或Object.Equals
(取决于T
类型的元素是否实现IEquatable<T>
)。
为什么这些集合(在测试默认相等时)不直接调用EqualityComparer<T>.Default
或IEquatable<T>.Equals
而不是Object.Equals
类?< / p>
答案 0 :(得分:25)
关于你的第一个问题:
它说的实际上没用,因为IEqualityComparer<T>
接口的两个方法都标记为抽象。它们都没有“默认”实现,无论如何你都必须覆盖它。如果有的话,他们在这里提供的“推理”听起来更像是比较者可能所做的事情的指导,而且与实际做的无关。
查看EqualityComparer<T>
类的公共/受保护接口,只有一个兑换质量,它实现了非通用IEqualityComparer
接口。我认为他们的意思是他们推荐它,因为EqualityComparer<T>
实际上实现了非通用IEqualityComparer
接口,因为在需要非通用比较器的情况下可以使用您的类。
在IComparer<T>
的评论部分中更有意义:
我们建议您从Comparer&lt; T&gt;中获得。而不是实现IComparer&lt; T&gt;接口,因为Comparer&lt; T&gt; class提供IComparer.Compare方法的显式接口实现和获取对象的默认比较器的Default属性。
我怀疑它应该为IEqualityComparer<T>
说些类似的东西,但是有些想法混杂在一起并最终得到了不完整的描述。
关于你的第二个问题:
图书馆中找到的馆藏的主要目标是尽可能灵活。一种方法是通过提供IComparer<T>
或IEqualityComparer<T>
进行比较,允许自定义方式比较其中的对象。如果没有提供默认比较器的实例,那么获取默认比较器的实例比直接进行比较要容易得多。反过来,这些比较器可能包含调用恰当比较的必要逻辑。
T
是否为IEquatable<T>
并在对象上调用IEquatable<T>.Equals
或以其他方式使用Object.Equals
。更好地封装在比较器中,而不是在集合代码中可能重复。
此外,如果他们想要直接调用IEquatable<T>.Equals
,他们必须在T
上添加一个限制才能使此调用成为可能。这样做会降低其灵活性,并且不利于首先提供比较器的好处。
答案 1 :(得分:3)
我不明白1的建议。对我来说这似乎很奇怪。
至于2 - 很多时候,你最终会得到一个Dictionary
的类型(例如IEqualityComparer<T>
)。虽然实现可以存储一个空值并明确地调用Equals
本身,但这样做会很痛苦 - 并且还会涉及到显着的丑陋以确保它没有框值不必要地实施IEquatable<T>
的类型。使用界面,EqualityComparer<T>.Default
显着更简单,更一致。
答案 2 :(得分:0)
从基类派生类的主要原因是基类 class可以提供可以重用的代码,因此您不必编写代码 自己。
如果你从界面派生你的比较器,你必须创建 代码,你自己给你一个默认的比较器(当然只有你需要它,但嘿,每个人都想要免费的功能!)
班级EqualityComparer使用factory design pattern。
在Factory模式中,我们创建对象而不将创建逻辑暴露给客户端,并使用通用接口引用新创建的对象。
好消息是,EqualityComparer的所有用户只需调用prperty default,并为他们完成所有操作以创建暴露接口的正确对象IEqualtiyComparer
这样做的好处是,如果你需要IEqualityComparer作为函数中的参数,那么你不必检查类T
是否实现IEqualtiy<T>
,字典就是这样做的对你而言。
如果您派生自EqualtityComparer<T>
并确保派生类遵循工厂设计模式,那么在几个相同的比较器之间切换很容易。
此外,与任何工厂一样,您只需更改工厂的参数即可生成完全不同的等式比较器。
当然你可以在不派生EqualtyComparer<T>
的情况下创建一个相等比较器工厂,但是如果你派生你的工厂可以创建一个额外类型的相等比较器:默认的相等比较器,它是使用eiether { {1}}或Object.Equals。您不必为此编写任何额外的代码,只需派生!
你是否觉得从EqualtyComparer派生是否有用,取决于你是否认为工厂设计模式是有用的。
例如,假设您要检查两个字典是否相等。人们可以想到几个层次的平等:
IEquatable<T>
的默认相等比较器如果从EqualityComparer派生字典比较器类,则已经有comparer(1)。如果提供的TValue比较器来自EqualityComparer,则(3)和(4)之间没有真正的区别。
因此,让我们派生出可以创建这四个比较器的工厂:
TValue
对于comparer(2),您可以使用stackoverflow IEqualityComparer that uses ReferenceEquals中描述的参考值比较器
所以现在我们有四个不同的相等比较器,只为一个比较器提供代码。剩下的就被重复使用了!
如果没有创建默认比较器的工厂,这种重用就不那么容易了
比较器代码(4):使用提供的比较器检查TValue的等式
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);
}
}
现在我们可以简单地使用不同的比较器比较两个词典:
// 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;
}
因此推导导致我的相等比较工厂总是有一个合适的Defautlt比较器。让我自己编写代码
答案 3 :(得分:0)
如果您在MSDN解释中更改一个词,即将从派生为 use ,这将变得更加有意义。
在MSDN上: EqualityComparer<T>
我们建议您(而非派生)使用类,而不要实现EqualityComparer<T>
接口,因为IEqualityComparer<T>
类会进行测试使用EqualityComparer<T>
方法而不是IEquatable<T>.Equals
方法实现平等。这与Dictionary类和其他通用集合的Object.Equals
,Contains
,IndexOf
和LastIndexOf
方法是一致的。
当然,这仅在T实现Remove
请注意,奇怪的是,只有IEquality<T>
和Array
拥有List<T>
和IndexOf
方法,并且对于任何一种方法来说,没有重载占用LastIndexOf
。其他通用集合的构造函数采用IEqualityComparer<T>
在MSDN上: IEqualityComparer<T>
我们建议您(而非派生)从Comparer<T>:
类使用,而不要实现Comparer<T>
接口,因为IComparer<T>
类提供Comparer<T>
方法和Default属性的显式接口实现,该属性获取对象的默认比较器。
当然,这仅在T实现IComparer.Compare
或IComparable
如果T未实现从IComparable<T>
或EqualityComparer<T>
派生的所需接口,则很有用,因为它免费提供了非通用接口的实现。
另一方面,实现Comparer<T>
或IEqualityComparer<T>
可以提高性能,因为它可以跳过对IComparer<T>
或IEquatable<T>
的调用。