关于c#:Object.GetHashCode()是引用或值唯一的吗?

关于c#:Object.GetHashCode()是引用或值唯一的吗?

Is Object.GetHashCode() unique to a reference or a value?

关于Object.GetHashCode()的MSDN文档描述了该方法应如何工作的3个相互矛盾的规则。

  • 如果两个相同类型的对象表示相同的值,则哈希函数必须为每个对象返回相同的常数值。
  • 为了获得最佳性能,哈希函数必须为所有输入生成随机分布。
  • 无论对对象进行任何更改,哈希函数都必须返回完全相同的值。
  • 规则1和3与我矛盾。

    Object.GetHashCode()是否基于对象的值或对该对象的引用返回唯一数字。 如果我重写该方法,则可以选择要使用的方法,但是如果有人知道的话,我想知道内部使用的方法。


    Rules 1 & 3 are contradictory to me.

    在一定程度上,它们是。原因很简单:如果将对象存储在哈希表中,并且通过更改其值来更改其哈希,则哈希表将丢失该值,并且无法通过查询哈希表再次找到它。重要的是,尽管对象存储在哈希表中,但它们保留其哈希值。

    要实现这一点,通常最简单的做法是使可哈希对象不可变,从而避免整个问题。但是,仅使确定哈希值的那些字段不可变就足够了。

    考虑以下示例:

    1
    2
    3
    4
    5
    6
    7
    struct Person {
        public readonly string FirstName;
        public readonly string Name;
        public readonly DateTime Birthday;

        public int ShoeSize;
    }

    人们很少更改生日,大多数人从不更改姓名(结婚时除外)。但是,它们的鞋号可能会任意增大,甚至会缩小。因此,合理地使用生日和姓名来识别人物,而不是鞋子的大小。哈希值应反映以下内容:

    1
    2
    3
    public int GetHashCode() {
        return FirstName.GetHashCode() ^ Name.GetHashCode() ^ Birthday.GetHashCode();
    }

    不确定要使用的MSDN文档。查看有关Object.GetHashCode的当前文档(http://msdn.microsoft.com/zh-cn/library/system.object.gethashcode.aspx)提供了以下"规则":

    • 如果两个对象比较相等,则每个对象的GetHashCode方法必须返回相同的值。但是,如果两个对象的比较不相等,则两个对象的GetHashCode方法不必返回不同的值。

    • 只要没有修改确定对象的Equals方法返回值的对象状态,对象的GetHashCode方法就必须始终返回相同的哈希码。请注意,这仅适用于当前执行的应用程序,并且如果再次运行该应用程序,则可以返回不同的哈希码。

    • 为了获得最佳性能,哈希函数必须为所有输入生成随机分布。

    如果您指的是第二个要点,则此处的关键短语是"只要没有对对象状态进行任何修改"和"仅对于应用程序的当前执行为true"。

    同样从文档中

    A hash function is used to quickly generate a number (hash code) that corresponds to the value of an object. Hash functions are usually specific to each Type and must use at least one of the instance fields as input. [Emphasis added is mine.]

    至于实际的实现,它明确指出,当且仅当该派生类将值相等定义为引用相等并且类型不是值类型时,派生类才可以遵循Object.GetHashCode实现。换句话说,Object.GetHashCode的默认实现将基于引用相等性,因为没有要使用的实际实例字段,因此不能保证不同对象的唯一返回值。否则,您的实现应特定于您的类型,并应至少使用一个实例字段。例如,String.GetHashCode的实现针对相同的字符串值返回相同的哈希码,因此,如果两个String对象表示相同的字符串值,则它们返回相同的哈希码,并使用字符串中的所有字符生成该哈希值。


    规则1和3并不是真正的矛盾。

    对于引用类型,哈希码是从对对象的引用派生的-更改对象的属性,并且引用是相同的。

    对于值类型,哈希码从值派生,更改值类型的属性,您将获得值类型的全新实例。


    有关如何处理GetHashCode(超出Microsoft规则)的很好解释,在Eric Lipperts(C#的联合设计师)博客中提供了文章" GetHashCode的准则和规则"。 在此处添加超链接不是一个好习惯(因为它们可能会变得无效),但这是值得的,并且如果丢失了超链接,则上面的信息可能仍会找到它。


    我不确定在真正的.NET Framework中如何实现Object.GetHashCode,但是在Rotor中,它使用对象的SyncBlock索引作为哈希码。网络上有一些关于它的博客文章,但是大多数来自2005年。


    默认情况下,它基于对对象的引用进行操作,但这意味着它是完全相同的对象,因此两者将返回相同的哈希值。但是像字符串类一样,散列应该基于该值。" a"和" b"将具有不同的哈希,但是" a"和" a"将返回相同的哈希。


    推荐阅读