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()
没有被覆盖,您将获得该实现。
这非常有用,例如,如果您想构建一个引用相等查找,即使对于已覆盖 Equals
和 GetHashCode
的类型也是如此。我使用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.GetHashCode
以RuntimeHelpers.GetHashCode()
的形式记录在案,(IMO)会更加混乱。
【讨论】:
RuntimeHelpers.GetHashCode 处理 null,因此无需显式执行。 @MattSmith:嗯...不幸的是,据我所知,这没有记录,所以我不想依赖它 - 但我会添加评论。 好点。我实际上认为它是“记录在案的”,因为通常微软的文档在它抛出时会在文档中包含一个子句(例如:System.ArgumentNullException: o is null。)。但也许他们并不总是勤奋。 @MattSmith 实际上object.GetHashCode()
也处理返回 0 的空值,如果您使用的语言允许您在空实例上非虚拟调用实例方法。 (AFAIK,适用于所有实现)。当然,C# 不是这样的语言。以上是关于RuntimeHelpers.GetHashCode 有啥作用的主要内容,如果未能解决你的问题,请参考以下文章