【问题标题】:C# how to calculate hashcode from an object referenceC#如何从对象引用计算哈希码
【发布时间】:2011-02-26 01:12:51
【问题描述】:

伙计们,这是一个棘手的问题!

TickZoom 系统的一部分必须将每种对象的实例收集到 Dictionary 类型中。

它们的相等性和哈希码必须基于对象的实例,这意味着引用相等而不是值相等。挑战在于系统中的某些对象已覆盖 Equals() 和 GetHashCode() 以用作值相等,并且它们的内部值将随着时间而改变。这意味着它们的 Equals 和 GetHashCode 是无用的。如何通用而不是侵入性地解决这个问题?

到目前为止,我们创建了一个结构来包装每个名为 ObjectHandle 的对象,以便散列到字典中。正如您在下面看到的,我们实现了 Equals(),但如何计算哈希码的问题仍然存在。

public struct ObjectHandle : IEquatable<ObjectHandle>{
    public object Object;
    public bool Equals(ObjectHandle other) {
        return object.ReferenceEquals(this.Object,other.Object);
    }
}

看到了吗?有方法 object.ReferenceEquals() 将比较引用相等性,而不考虑对象中任何重写的 Equals() 实现。

现在,如何只考虑引用而不考虑任何覆盖的 GetHashCode() 方法来计算匹配的 GetHashCode()?

啊,我希望这能给你一个有趣的谜题。我们被困在这里了。

真诚地, 韦恩

【问题讨论】:

    标签: c# hash reference equals gethashcode


    【解决方案1】:

    RuntimeHelpers.GetHashCode() 正是这里所需要的。

    【讨论】:

    • 在对象的生命周期内返回值是否不变?如果是,那么这真是一件好事。
    • 是的。它是恒定的。 GC 保留对象的句柄。它的实际位置可以移动。所以它对它的句柄进行哈希处理。
    • @Wayne:人们可能期望事情会以这种方式工作,但事实并非如此。每个对象的标头包含 32 位,这些位可能或者保存应该由 RuntimeHelpers.GetHashCode() 返回的值,或者是包含许多事物的表的索引,其中包括 RuntimeHelpers.GetHashCode() 值(某些整数范围值用于不同的目的)。对象重定位是通过跟踪和更改对存在于宇宙中任何地方的对象的每个引用来处理的。
    • @Wayne:请注意,如果某个字段在某个时刻持有对宇宙中任何地方的某个对象的唯一引用(例如,String 的实例),则没有观察到代码可以已经在该时间点之前做出,这总是足以在以后确定该字段是否仍然引用同一对象,而不是引用包含相同数据的新对象。
    • RuntimeHelpers.GetHashCode(obj) 返回与 GetHashCode(obj) 相同的内容
    【解决方案2】:

    您正在打破模式,这会导致无法解决的问题。方法Equals 应该比较对象的内容,而不是比较引用。这就是object.Equals 所做的,为什么要使用相同的行为覆盖?

    现在关于GetHashCode。同样,哈希码是应用于对象内容的哈希函数。您不能仅通过参考来计算它。您可以使用指向对象的指针并将其用作哈希,但在 .net 中,对象的地址可以通过 GC 更改。

    【讨论】:

    • 你是对的。但是 TickZoom 做了一些非常先进和高性能的编程技术,通常只在 C++ 而不是 C# 中看到。在这种情况下,我们正在执行面向方面的编程,以输出 UML 序列格式的方法调用跟踪,以便 Trace2UML 工具自动生成 UML 序列图。方面必须记录它已经看到的每个对象,以便当它看到一个新对象时,它可以发出文本命令以在图表上绘制该实例。所以它与对象的内容无关。简单的内部几乎编译器之类的问题。
    • @Wayne:好的。如果您想在 Dictionary 中存储某些内容,那么您必须正确覆盖GetHashcode,否则您无法使用字典。好吧,你可以,但它变得低效。然后使用链表。
    • 正确答案如下,因为 GC 对每个项目都有唯一的句柄,但不会与您共享。 .NET 为默认对象提供 Equals() 和 GetHashCode() 如果您不覆盖基于该句柄的默认对象并且仅比较引用相等性。因此,如果您不覆盖它们,这就是您所得到的。我的需要是使用原始的默认 Equals 和 GetHashCode,而 .NET 提供了对它们的访问权限,以绕过使用 RuntimeHelpers.Equals 和 RuntimeHelpers.GetHasCode 覆盖它们的人。
    • @Wayne 我有一种感觉,您可以通过某种方式获得句柄或句柄哈希。现在我知道 RuntimeHelpers.GetHashCode() 可以做到:)
    • @Wayne:在某些框架中,GC 为每个项目保留一个唯一句柄,该句柄在其生命周期内永远不会改变,但对于 .NET 的常见实现来说并非如此。每个对象都有一个唯一的地址,但是当 GC 重新定位一个对象时,它会跟踪每个引用并更改它以反映新地址。当一个对象第一次调用默认的GetHashCode时,系统会任意选择一个数字并存储在一个字中,与其他一些位共享空间;没有特别的理由期望该数字不会与其他活动对象发生冲突。
    【解决方案3】:

    哈希码不必是唯一的。 (但唯一性会提高性能)。
    所以 - 你可以做的一件事是通过类型名称设置哈希码。 同一类型的所有对象都将具有相同的哈希码。

    【讨论】:

    • 如果哈希码是常量,为什么还要使用它?它会生成任何 Dictionary 链表。
    • 正确。 MSDN 说哈希码应该随机分布在值的范围内以获得最佳性能。请记住,可能需要以两种方式使用对象。通过它的值进行散列,并且在不同的上下文中,仅通过它的引用进行散列。这就是这种情况下的问题。我找到了答案,但将其作为答案单独发布。希望你也受益。
    猜你喜欢
    • 2010-09-11
    • 1970-01-01
    • 2017-02-04
    • 2011-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-29
    • 2016-06-23
    相关资源
    最近更新 更多