【问题标题】:When implementing IEqualityComparer<T>.GetHashCode(T obj), can I use the current instance's state, or do I have to use obj?在实现 IEqualityComparer<T>.GetHashCode(T obj) 时,我可以使用当前实例的状态,还是必须使用 obj?
【发布时间】:2014-09-16 20:36:34
【问题描述】:

我实现IEqualityComparer时怎么会有GetHashCode(T obj)的参数?它当然不是静态对象,那为什么我不能只使用当前实例的状态来生成哈希码呢?是this == obj吗?

我很好奇,因为我正在尝试这样做:

public abstract class BaseClass : IEqualityComparer<BaseClass>
{
    public abstract int GetHashCode(BaseClass obj);
}

public class DerivedClass : BaseClass
{
    public int MyData;

    public override int GetHashCode(BaseClass obj)
    {
        return MyData.GetHashCode();
        // Or do I have to do this:
        // return (DerivedClass)obj.MyData.GetHashCode();
    }
}

我试图阻止进行强制转换,因为它被用于真正高性能的代码中。

【问题讨论】:

    标签: c# interface polymorphism


    【解决方案1】:

    我认为这里的主要问题是您将 IEqualityComparer&lt;T&gt;IEquatable&lt;T&gt; 混淆了。

    IEquatable&lt;T&gt; 定义了一种方法,用于确定当前实例 (this) 是否等于同一类型的实例。换句话说,它用于测试objA.Equals(objB)。在实现该接口时,建议您也重写GetHashCode()实例方法。

    IEqualityComparer&lt;T&gt; 定义了测试给定类型的两个对象是否相等的方法,换句话说,它用于测试comparer.Equals(objA, objB)。因此有必要提供一个对象作为GetHashCode 的参数(记住它不同于它从object 继承的GetHashCode

    您可以将IEquatable&lt;T&gt; 视为您的对象的表达方式,“这就是我知道我是否与其他人平等的方式,”和IEqualityComparer&lt;T&gt; 是您的对象的表达方式, “这就是我如何知道其他两件事是否相等”。

    有关如何在框架中使用这两个接口的一些很好的示例,请参阅:

    您是否应该使用IEqualityComparer&lt;T&gt; 的当前状态来确定哈希码?如果状态完全可变,那么 no! 任何使用哈希的地方(例如HashSet&lt;T&gt;Dictionary&lt;T, V&gt;)都会缓存哈希码并用于高效查找。如果该哈希码可以因为比较器的状态改变而改变,那将完全破坏存储哈希的数据结构的有用性。现在,如果状态是不可变的(即它仅在创建比较器时设置,并且在比较器的整个生命周期内都不能修改),那么是的,你可以,但我仍然建议不要这样做,除非你有一个很好的理由.

    最后,您提到了性能。老实说,这听起来像premature optimization。我建议不要太担心性能,直到您可以确定这行特定的代码导致了问题。

    【讨论】:

    • 什么时候调用 object.GetHashCode(),什么时候调用 IEquatable.GetHashCode()?此外,仅在创建实例时设置状态。我基本上是想让我的类尽可能地像一个值类型。
    • GetHashCode()(来自任一接口)在您需要快速比较两个对象或将对象存储在HashSet&lt;T&gt; 中时调用(因为它稍后会使用散列进行快速比较)。区别在于object.GetHashCode() 返回当前对象的哈希值,而IEqualityComparer&lt;T&gt;.GetHashCode(T) 返回给定对象的哈希值。
    • 所以如果我将这些放在 HashSet 中,我应该怎么做才能确保只添加具有唯一类型和状态的实例?如果我有两个具有相同 MyData 的 DerivedClass 实例,则只应添加一个。
    • 最基本的规则是,如果两个对象相等,那么它们必须具有相同的哈希码(两个不相等的对象可能也有相同的哈希码)。除此之外,你可以用任何你喜欢的方式实现它——尽管实现一个 good 散列函数要困难得多,并且很大程度上取决于你的类型的设计和预期用途。这适用于您实施IEquatable&lt;T&gt;IEqualityComparer&lt;T&gt;。同样,在您确定这是性能问题之前,我不会关注它。
    【解决方案2】:

    如果您不使用来自obj 参数的信息,您的哈希码不会因不同的传入对象而有所不同,并且不会有用。比较器不是您要获取哈希码或与之比较的对象的实例。

    确实,您可以在GetHashCode 中使用comaprer 的本地字段,甚至可以将MyData 作为您的示例中所示的哈希码返回 - 它仍将满足GetHashCode 要求“为同一对象返回相同的值数据”。但是在您的示例中,所有哈希码对于比较器的实例都是相同的,因此将其用于Dictionary 基本上会将字典转换为列表。

    这同样适用于Equals 调用——确实你可以一直返回true,但它会有多大用处?

    【讨论】:

    • 好吧,在我的例子中,我希望相等的行为像结构一样:具有相同状态的不同实例应该是相等的。我不只使用结构的原因是因为我需要一些多态性的东西。
    • @terence - p.s.w.g.有非常好的答案(确实是+1),希望能澄清事情。看待IEqualityComparer 的另一种方式是将其视为 2 个静态辅助函数 - 在这种心态下,您可能更明显地必须使用来自传入对象的信息。
    猜你喜欢
    • 1970-01-01
    • 2012-10-23
    • 1970-01-01
    • 2011-08-20
    • 2017-09-30
    • 1970-01-01
    • 1970-01-01
    • 2021-03-13
    • 2011-12-27
    相关资源
    最近更新 更多