RuntimeHelpers.GetHashCode 有啥作用

Posted

技术标签:

【中文标题】RuntimeHelpers.GetHashCode 有啥作用【英文标题】:What does RuntimeHelpers.GetHashCode doRuntimeHelpers.GetHashCode 有什么作用 【发布时间】:2012-06-29 16:49:51 【问题描述】:

RuntimeHelpers.GetHashCode(object) 方法允许根据对象的身份生成哈希码。 MSDNstates:

RuntimeHelpers.GetHashCode 方法总是调用 Object.GetHashCode 方法是非虚拟的,即使对象的类型有 重写了 Object.GetHashCode 方法。

[MethodImpl(MethodImplOptions.InternalCall)]
[SecuritySafeCritical]
public static extern int GetHashCode(object o);

但是,当使用 Reflector (.NET 4.0) 检查 Object.GetHashCode() 方法时,我们将看到以下代码:

public virtual int GetHashCode()

    return RuntimeHelpers.GetHashCode(this);

这让我相信 MSDN 文档是错误的,因为从 RuntimeHelpers.GetHashCode(object) 中调用 Object.GetHashCode 会导致堆栈溢出。

那么RuntimeHelpers.GetHashCode(object) 的实际行为是什么,它是如何工作的?它是如何计算哈希的?

【问题讨论】:

@dtb 在某些地方 C# 对虚拟方法进行非虚拟调用 - 例如 base.*。在大多数情况下,在虚拟方法上使用 call 会使 CLI 对您大声喊叫 - 这是不允许的,无论调用者的语言如何。 【参考方案1】:

奇怪,当我通过 Reflector 查看 System.Object.GetHashCode 时,我看到了

public virtual int GetHashCode()

    return InternalGetHashCode(this);

对于运行时助手:

public static int GetHashCode(object o)

    return object.InternalGetHashCode(o);

也许这是一个框架差异?我正在查看 2.0 程序集。

【讨论】:

不要删除这个答案。它表明实现可能会有所不同,但行为仍与指定的一样。 (Skeet 的回答中提到了你的回答)。【参考方案2】:

我认为 MSDN 文档试图描述 行为,而不是实现。关键点:RuntimeHelpers 返回 default 实现,如果 object.GetHashCode() 没有被覆盖,您将获得该实现。

这非常有用,例如,如果您想构建一个引用相等查找,即使对于已覆盖 EqualsGetHashCode 的类型也是如此。我使用RuntimeHelpers.GetHashCode()Object.ReferenceEquals 在我维护的序列化程序中执行此操作。

【讨论】:

所以,文档有点误导,或者我只是解释不正确? @Steven 是的,说它“以非虚拟方式调用 Object.GetHashCode 方法”有点误导,尤其是考虑到此处显示的其他两个实现(在您的帖子中和“Me.Name”的帖子) @Jon yes,但基于这篇文章中的两个反射器转储和一个答案,它实际上并不是 IL 的实现方式。 @MarcGravell:是的,我将在我的回答中而不是在 cmets 中详细说明 :) @Jon 我会期待的;p【参考方案3】:

从您自己的问题来看,RuntimeHelpers.GetHashCode(Object) 确实是非覆盖 Object.GetHashCode() 的实现。

【讨论】:

【参考方案4】:

关键是object.GetHashCode() 可以被覆盖 - 并且经常被覆盖,例如通过string。这意味着您无法找出object.GetHashCode()默认 实现返回的“身份哈希码”。

如果您想实现一个考虑对象身份的相等比较器(例如,对于HashSet),这可能很有用。

例如:

public class IdentityComparer<T> : IEqualityComparer<T> where T : class

    public bool Equals(T x, T y)
    
        // Just for clarity...
        return object.ReferenceEquals(x, y);
    

    public int GetHashCode(T x)
    
        // The nullity check may be unnecessary due to a similar check in
        // RuntimeHelpers.GetHashCode, but it's not documented
        return x == null ? 0 : RuntimeHelpers.GetHashCode(x);
    

然后:

string x = "hello";
string y = new StringBuilder("h").Append("ello").ToString();
Console.WriteLine(x == y); // True (overloaded ==)
Console.WriteLine(x.GetHashCode() == y.GetHashCode()); // True (overridden)

IdentityComparer<string> comparer = new IdentityComparer<string>();
Console.WriteLine(comparer.Equals(x, y)); // False - not identity

// Very probably false; not absolutely guaranteed (as ever, collisions
// are possible)
Console.WriteLine(comparer.GetHashCode(x) == comparer.GetHashCode(y));

编辑:只是为了澄清一点......

那么 RuntimeHelpers.GetHashCode(object) 的实际行为是什么,它是如何工作的?

观察到的行为是从RuntimeHelpers.GetHashCode(object) 返回的值与从对Object.GetHashCode() 的非虚拟调用返回的值相同。 (你不能轻易用 C# 编写那个非虚拟调用。)

至于它是如何工作的 - 这是一个实现细节:) IMO 以哪种方式发生事情并不重要(什么叫什么)。重要的是记录在案的行为,这是正确的。哎呀,不同版本的 mscorlib 可以以不同的方式实现这一点——从用户的角度来看,这根本不重要。如果没有反编译,你应该无法区分。

如果Object.GetHashCodeRuntimeHelpers.GetHashCode() 的形式记录在案,(IMO)会更加混乱。

【讨论】:

RuntimeHelpers.GetHashCode 处理 null,因此无需显式执行。 @MattSmith:嗯...不幸的是,据我所知,这没有记录,所以我不想依赖它 - 但我会添加评论。 好点。我实际上认为它是“记录在案的”,因为通常微软的文档在它抛出时会在文档中包含一个子句(例如:System.ArgumentNullException: o is null。)。但也许他们并不总是勤奋。 @MattSmith 实际上object.GetHashCode() 也处理返回 0 的空值,如果您使用的语言允许您在空实例上非虚拟调用实例方法。 (AFAIK,适用于所有实现)。当然,C# 不是这样的语言。

以上是关于RuntimeHelpers.GetHashCode 有啥作用的主要内容,如果未能解决你的问题,请参考以下文章