为什么IEqualityComparer <t>需要GetHashCode?</​​ t>

时间:2014-05-10 20:57:56

标签: .net

我之前已经知道这个问题的变化,但我还没有发现任何具体针对我所问的问题。

在.NET中,我们有IEqualityComparer<T>,其中包含以下内容:

public interface IEqualityComparer<T>
{
    public bool Equals(T x, T y);
    public int GetHashCode(T obj);
}

所以,我的问题是为什么不将它分成两个接口,分别是:

public interface IEqualityComparer<T>
{
    public bool Equals(T x, T y);
}

public interface IHashProvider<T> : IEqualityComparer<T>
{
    public int GetHashCode(T obj);
}

换句话说,我可以看到为什么在对象上执行GetHashCode的任何情况下,你几乎肯定也需要知道如何执行Equals。此外,Equals的定义是正确实现GetHashCode的要求之一(如果两个对象相等,它们的哈希码也必须相等)。

然而,反过来看,这似乎并非如此。想象一下Equals的某些自定义实现检查两个对象是否相等,但是没有执行涉及哈希码的任何操作的情况并不难想象。

是否有任何理由将界面拆分为与上述类似,允许方法需要IHashProvider<T>(或任何它将被称为)或IEqualityComparer<T>取决于它们是否会或者不会使用哈希码?这只是一个历史性的决定,现在改变太麻烦了吗?或者是否有一些原因让我忽略了总是GetHashCode旁边实施Equals这一点非常重要?

3 个答案:

答案 0 :(得分:3)

IEqualityComparer<T>添加到.NET框架的原因是,如果您无法使用不同的Equals覆盖修改类,则允许您自定义基于哈希的容器的逻辑/ GetHashCode(例如,它在其他人的库中)或者您不希望因任何其他原因更改默认实现 - 例如,为了向后兼容。这是拥有此接口的主要目的:您将其提供给哈希容器,并使用它而不是对象附带的Equals / GetHashCode逻辑。

平台设计人员似乎没有自定义哈希容器之外的IEqualityComparer<T>接口的用例。当外部提供的Equals本身没有GetHashCode时,很难想出一个场景。 .NET平台已经为需要通过Predicate<T1,T2>外部化双变量(或N变量)检查的用户提供了一种简洁的机制,所以如果你想编写从外部获取相等检查器的代码,你可以这样做:

void MyFunction(IEnumerable<T> one, IEnumerable<T> two, Predicate<T,T> equalityCheker) {
    foreach (var a in one) {
        foreach (var b in two) {
            if (equalityCheker(a, b)) {
                Console.WriteLine("Equal: {0} {1}", a, b);
            }
        }
    }
}

答案 1 :(得分:2)

原因是添加两个单独的接口而不是一个接口是不值得的。我强烈建议您阅读Eric Gunnerson撰写的名为“Minus 100 points”的文章,其中他讨论了向C#添加功能的成本。归结为将GetHashCode()Equals()分开并没有得到足够的证据来保证它是独立的界面。

我还建议阅读一些类似主题的文章

答案 2 :(得分:0)

如果你想比较两个不可变的对象是否相等,那么 可能 要更快,首先检查它们是否具有相同的哈希值码。 - 由于对象是不可变的,因此它们的哈希码是常量,因此它也可以保存为对象状态的一部分,以便在字典中更快地使用。

当您在字典中搜索对象时,搜索是通过密钥的哈希码完成的,以找到正确的bin,然后使用Equals()来查找正确的密钥。

换句话说,如果要查找或比较对象,通常首先要比较它们的哈希码是很重要的,因此相等接口也应该能够获取哈希码。