【问题标题】:Do I need to override GetHashCode() on reference types?我需要在引用类型上覆盖 GetHashCode() 吗?
【发布时间】:2010-10-20 15:31:30
【问题描述】:

我在 StackOverflow 上阅读了大多数关于 GetHashCode 的问题。但我仍然不确定是否必须在引用类型上覆盖 GetHashCode。我从某人在另一个问题中的回答中摘录了以下内容:

Object.GetHashCode() 使用内部 System.Object 类中的字段 生成哈希值。每个对象 created 被分配了一个唯一的对象 键,存储为整数,当它是 创建的。这些键从 1 开始, 每次增加一个新对象 任何类型都会被创建。

如果在 .NET Framework 3.5 中仍然如此(有人可以确认吗?),那么我看到的引用类型的默认实现的唯一问题是哈希码的分布很差。

我将分解我的问题:

a) 因此,如果GetHashCode 用于Dictionary,建议也覆盖它,或者默认实现是否执行得很好?

b) 我有引用类型,因为它们具有唯一标识它们的字段,所以很容易做到这一点,但是那些所有成员也是引用类型的引用类型呢?我应该在那里做什么?

【问题讨论】:

    标签: c# .net dictionary gethashcode


    【解决方案1】:

    如果您覆盖 Object.Equals(),您只需在引用类型上覆盖 GetHashCode()。

    原因很简单——通常,2 个引用总是不同的(a.Equals(b)==false,除非它们是同一个对象)。在这种情况下,GetHashCode() 的默认实现将提供 2 个不同的哈希值,所以一切都很好。

    但是,如果您覆盖 Equals(),则无法保证此行为。如果两个对象相等(根据 Equals()),您需要保证它们与 GetHashCode 具有相同的哈希码,因此您应该覆盖它。

    【讨论】:

    • 附带说明,如果您在引用类型上覆盖 GetHashCode,最好确保该对象是不可变的。否则,如果它的内容发生变化,你可能会在 HashTable 中丢失它。
    【解决方案2】:

    我只是做了一个样本测试,我看不出它是如何从 1 开始并递增的。

    for (int i = 0; i < 16; i++)
    {
        object obj = new object();
        Console.Write(obj.GetHashCode() + " ");
    }
    

    这些结果:

    45653674 41149443 39785641 45523402 35287174 44419000 52697953 22597652 
    10261382 59109011 42659827 40644060 17043416 28756230 18961937 47980820
    

    事实上,使用 Reflector,我只能看到这个:

    internal static extern int InternalGetHashCode(object obj);
    

    所以它究竟是如何发生的对我来说是一个谜(可能有一个模式,但我现在不打算深入挖掘——也许是某种“伪随机数”algorithm?)。 CLR 团队的某个人可以回答这个问题。

    至于其他问题,Reed 实际上击败了我:GetHashCodeEquals。为了以防万一,MSDN page 用一些血淋淋的细节对其进行了描述。

    【讨论】:

    • 这取决于...在 .NET 1.1 和 1.0 中,我们有比 .NET 2.0 中的其他算法。如果我没记错的话,这是 2.0 中的随机生成器。
    • 是的,我没有看到它在任何早期版本中的外观。我刚刚按照他的要求测试了 3.5。是的,我认为它是一个随机生成器(因此我链接到一个)。
    • InternalGetHashCode 映射到 CLR 中的一个 ObjectNative::GetHashCode 函数,其内容在this article的置顶答案中可见。
    猜你喜欢
    • 2011-01-08
    • 1970-01-01
    • 1970-01-01
    • 2012-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多