如果我的类实现了IEqualityComparer,我应该实现非通用的GetHashCode和Equals<T>吗?



如果我的类实现了IEqualityComparer<T>,我应该实现非泛型GetHashCodeEquals吗?

更新:我希望 MS 在引入 IEqualityComparer<T> 时更新了他们集合的实现。所以我认为 Dictionary 和任何其他集合类将在内部检查我的类是否实现了IEqualityComparer<T>并且仅在未实现该接口时才使用GetHashCodeEquals的泛型方法。如果没有集合支持,该接口几乎没有价值。

更新2:我刚刚使用 ILSpy 检查了Dictionary.FindEntry(TKey key)。它使用IEqualityComparer<TKey>(可变量comparer如下)。事实上,我根本没有发现任何使用非通用GetHashCodeEquals函数。

int num = this.comparer.GetHashCode(key) & 2147483647;
for (int i = this.buckets[num % this.buckets.Length]; i >= 0; i = this.entries[i].next)
{
    if (this.entries[i].hashCode == num
                && this.comparer.Equals(this.entries[i].key, key))
    {
        return i;
    }
}

所以似乎我的类只需要实现IEqualityComparer<T>就可以与Dictionary正确使用。

我知道,以防万一,实现非通用功能不会有什么坏处。

但是,如果时间没有带来任何价值,我们应该花时间吗?

我将使我的问题更具体:

我是否应该实现非泛型GetHashCodeEquals如果我的类实现了IEqualityComparer<T>并且:

  1. 我不在我的代码中使用非泛型集合
  2. 第三方代码不调用我的代码的GetHashCodeEquals方法。

Microsoft代码是否仍然需要非通用版本才能正常工作?

更新3:我想我明白了。我以为IEqualityComparer<T>要在我的课堂上实现。在这种情况下,我们将在一个地方拥有方法的通用版本和非通用版本。这不是应该如何使用IEqualityComparer<T>。它应该作为单独的类实现并用作参数。

谢谢大家。

这取决于。 IEqualityComparer<T>用于比较类型T的两个实例 - 通常由单独的比较器类实现。 通常,您不会在类类型T中实现这一点。 它旨在提供另一种比较,以便与支持他的类型一起使用。

如果你在类本身中实现这一点,你通常会实现IEquatable<T> .

话虽如此,覆盖 Object.Equals 通常非常简单,因为您可以使用 IEquatable<T>.Equals 方法来实现Object.Equals。 这使得实施起来"便宜"。 由于可以使用Object.Equals,它将为平等提供一致的含义,因此实施起来通常是一个好主意。

如果您的对象将用作哈希中的键,例如Dictionary<T,U>HashSet<T>,则应覆盖GetHashCode。 如果甚至有可能以这种方式使用它,则覆盖此方法是有益的。 通常,我发现在实现相等时重写GetHashCode很有用,以防万一我稍后将该类型用作键。

IEqualityComparer旨在替换GetHashCode的默认实现,并且每个.NET对象已经具有Equals。此接口仅由字典和 (Hash)Set 使用不同的哈希和比较方案,因为对象默认使用。

如果你的对象在字典和哈希表中用作键,你应该首先覆盖 Equals 和 GetHashCode,让 Sets/Dictionaryionary 使用默认比较器(你称之为 EqualComparere.Default 的比较器)作为你的对象,无论如何都会调用对象的 Equals 和 GetHashCode。

通过IEqualityComparer提供外部比较器的唯一原因是使用不同的比较方案。 例如,对于字符串,您可以从 BCL StringComparer 类中选择区分大小写和不区分大小写的变体。

更新1

这个问题的目的是为什么 List 和其他集合始终使用默认比较器,而不是对象提供的比较器。如果一个对象已经实现了Equals和GetHashCode,如果对象同时实现了IEqualityComparer,为什么List不应该使用它们?由于 List 没有提供允许使用不同比较器的 ctor,因此它确实需要使用默认比较器。

但是,如果您想使用不同的比较器,则始终可以使用 LINQ,它确实通过允许您显式传递自己的特定方法的比较器来解决此问题。 例如,Enumerable.Contains有一个重载,您可以在其中传递自己的比较器。

从 MSDN 示例:

Product[] fruits = { new Product { Name = "apple", Code = 9 }, 
                       new Product { Name = "orange", Code = 4 }, 
                       new Product { Name = "lemon", Code = 12 } };
Product apple = new Product { Name = "apple", Code = 9 };
Product kiwi = new Product {Name = "kiwi", Code = 8 };
ProductComparer prodc = new ProductComparer();
bool hasApple = fruits.Contains(apple, prodc);
bool hasKiwi = fruits.Contains(kiwi, prodc);

如果我理解正确,并且您问是否还必须实现IEqualityComparer是否实现IEqualityComparer<T>,那么答案是您不必这样做,但这可能是一个好主意。它只会使您的比较器与现有代码更兼容。

最新更新