在C#中使用IEqualityComparer 接口和EqualityComparer 类
我正在编写一个循环链表,使用这篇文章作为帮助。
在一个函数中,在此列表中搜索具有给定值的节点
public Node Find(T item) { Node node = FindNode(head, item); return node; } Node FindNode(Node node, T valueToCompare) { Node result = null; if (comparer.Equals(node.Value, valueToCompare)) result = node; else if (result == null && node.Next != head) result = FindNode(node.Next, valueToCompare); return result; }
作者使用IEqualityComparer comparer
对象,该对象在其中一个构造函数中使用属性EqualityComparer.Default
进行初始化。 你能解释一下在这里使用这些接口( IEqualityComparer
)和类( EqualityComparer
)的想法吗? 我读过MSDN,但我不理解工作和使用它们的原理。
IEqualityComparer
是一个接口,用于处理集合的相等比较。 您的collections会将等同性比较委托给此界面。 您可能会问,为什么不调用Equals
方法?
因为可以有几种可能的比较。 让我们举一个简单的例子: "Abc"
和"ABC"
相等? 这取决于。 "Abc".Equals("ABC") == false
但是如果你想要不区分大小写怎么办?
这就是为什么您的集合应该将相等比较委托给不同的类。 通过编写类,您将尊重单一责任原则:您的集合知道如何存储项目,并且相等比较器知道它们是否相等。
集合的示例:
var caseSensitive = new HashSet(StringComparer.Ordinal) // The default anyway { "Abc", "ABC" }; var caseInsensitive = new HashSet (StringComparer.OrdinalIgnoreCase) { "Abc", "ABC" };
结果将是:
caseSensitive.Count == 2 caseInsensitive.Count == 1 caseSensitive.Contains("aBc") == false caseInsensitive.Contains("aBc") == true
在这里,您使用相同的HashSet
类有两个完全不同的set语义。
现在, IEqualityComparer
什么?
-
bool Equals(T x, T y);
:此方法正如您所期望的那样:如果x
应该被视为等于y
则返回true
。 就像数学上的平等一样,它必须是:- 反身:
Equals(x, x) == true
- 对称:
Equals(x, y) == Equals(y, x)
- 传递:如果
Equals(x, y) && Equals(y, z)
则Equals(x, z)
- 反身:
-
int GetHashCode(T obj);
这个可能更难以正确。 对于每个obj
,它应该返回具有以下属性的哈希码:- 它永远不应该改变
- if
Equals(x, y)
则GetHashCode(x) == GetHashCode(y)
- 应该尽可能少的碰撞
注意,这并不意味着如果GetHashCode(x) == GetHashCode(y)
则Equals(x, y)
。 两个对象可以具有相同的哈希码但是不相等(毕竟最多可以有0xFFFFFFFF
哈希码)。
集合通常使用哈希代码来组织其项目。 例如, HashSet
将知道如果两个对象不具有相同的哈希码,则它们将不相等,因此可以相应地组织其桶。 哈希码只是一种优化。
现在,什么是EqualityComparer
? 它是IEqualityComparer
一个IEqualityComparer
快捷方式,它将使用对象自己的Equals
和GetHashCode
函数。 这是一个很好的默认值,因为这是你想要在大多数时间做的事情:虽然字符串可以有多个自然比较类型,但例如整数不是这种情况。
EqualityComparer
将处理几个特殊情况:
- 如果
T is IEquatable
,它将使用IEquatable
接口 - 如果
T is Nullable
且U is IEquatable
则它将正确处理该情况 - 它将针对一些特殊情况进行优化:
byte[]
和int-basdEnum
。
- 为什么C#7 ValueTuples实现了Equals方法而不是double equals操作符?
- 事件日志写入错误
- 可以调用Assembly.Load(byte )来引发AppDomain.AssemblyResolve事件吗?
- 如何为加密算法创建加密密钥?
- 如何强制quartz.net作业在完成后重新启动intervall
- 暂时更改Sitecore项目的布局
- 我可以在WinRT Metro Style Applications for Windows 8上使用我现有的.Net 4.0类库
- 获取驱动器和目录的图标:Icon.ExtractAssociatedIcon(filePath)不起作用?
- 为什么TaskFactory.StartNew收到CancellationToken