使用IEqualityComparer<;T>;接口和EqualityComparer<;T>;类

本文关键字:gt lt EqualityComparer 接口 使用 IEqualityComparer | 更新日期: 2023-09-27 18:25:33

我正在使用本文作为帮助编写一个循环链表。

在这个列表中搜索具有给定值的节点的函数中

    public Node<T> Find(T item)
    {
        Node<T> node = FindNode(head, item);
        return node;
    }
    Node<T> FindNode(Node<T> node, T valueToCompare)
    {
        Node<T> 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<T> comparer对象,该对象在一个构造函数中用属性EqualityComparer<T>.Default初始化。你能给我解释一下在这里使用这些接口(IEqualityComparer<T>)和类(EqualityComparer<T>)的想法吗?我读过MSDN,但不了解使用它们的原理。

使用IEqualityComparer<;T>;接口和EqualityComparer<;T>;类

IEqualityComparer<in T>是一个接口,用于处理集合的相等性比较。您的集合将委托此接口进行相等性比较。您可能会问,为什么不直接调用Equals方法呢?

因为可以有几种可能的比较。举一个简单的例子:"Abc""ABC"相等吗?这取决于情况。"Abc".Equals("ABC") == false,但是如果您想要不区分大小写呢?

这就是为什么您的集合应该将相等性比较委托给不同的类。通过组合类,您将遵守单一责任原则:您的集合知道如何存储项,相等比较器知道它们是否相等。

集合示例:

var caseSensitive = new HashSet<string>(StringComparer.Ordinal) // The default anyway
{
    "Abc", "ABC"
};
var caseInsensitive = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
    "Abc", "ABC"
};

结果将是:

caseSensitive.Count == 2
caseInsensitive.Count == 1
caseSensitive.Contains("aBc") == false
caseInsensitive.Contains("aBc") == true

这里有两个使用相同HashSet类的完全不同的集合语义。

现在,IEqualityComparer<in T>中有什么?

  • 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,它应该返回一个具有以下属性的哈希代码:
    • 它永远不会改变
    • 如果Equals(x, y),则GetHashCode(x) == GetHashCode(y)
    • 应该尽可能少地发生碰撞

注意,这并不意味着如果CCD_ 24则CCD_。两个对象可以具有相同的哈希码,但不相等(毕竟最多可以有0xFFFFFFFF个可能的哈希码)。

集合通常使用哈希代码来组织其项目。例如,HashSet将知道,如果两个对象没有相同的哈希代码,它们将不相等,因此可以相应地组织其bucket。哈希码只是一种优化。

现在,什么是EqualityComparer<T>.Default?这是IEqualityComparer<T>的常规快捷方式,它将使用对象自己的EqualsGetHashCode函数。这是一个很好的默认值,因为这是你大多数时候想要做的:虽然字符串可以有多个自然比较类型,但对于整数来说就不是这样了。

EqualityComparer<T>.Default将处理几个特殊情况:

  • 如果是T is IEquatable<T>,它将使用IEquatable<T>接口
  • 如果是T is Nullable<U>U is IEquatable<U>,它将正确处理该情况
  • 它将针对几个特殊情况进行优化:CCD_ 37和int based CCD_