【问题标题】:Is it totally fine to use a mutable object as a key in a Dictionary?使用可变对象作为字典中的键完全可以吗?
【发布时间】:2011-03-08 06:49:58
【问题描述】:

假设我有一些特殊的课程WrappedDataTable,我想将每个WrappedDataTable 与一个DataTable 关联起来。此外,对于任何给定的DataTable,我希望存在的WrappedDataTable 不超过一个。

一位同事建议我可以缓存我的WrappedDataTable 并使用工厂方法来访问它,如下所示:

public static class DataTableWrapper
{
    private Dictionary<DataTable, WrappedDataTable> _wrappedTables;

    static DataTableWrapper()
    {
        _wrappedTables = new Dictionary<DataTable, WrappedDataTable>();
    }

    public static WrappedDataTable Wrap(this DataTable table)
    {
        WrappedDataTable wrappedTable;
        if (!_wrappedTables.TryGetValue(table, out wrappedTable))
            _wrappedTables[table] = wrappedTable = new WrappedDataTable(table);

        return wrappedTable;
    }
}

起初这让我觉得很可疑,我猜是因为我已经熟悉字典中的键应该是不可变类型的想法。但也许情况不一定如此?一个快速的测试告诉我,DataTable 似乎在对其内容进行多次修改的过程中保持一致的哈希码;因此,Dictionary&lt;DataTable, TValue&gt; 似乎能够始终如一地为 ContainsKey 返回正确的值。

我想知道的是,object.GetHashCode 的基本版本是否默认会为每个单独的对象返回一个不变的值,或者我看到的 DataTable 是否只是一种错觉?

如果前者是真的——而且object.GetHashCode 工作得很好——那么“只使用不可变类型作为键”的建议似乎真的只适用于以下情况:

  1. 您希望对象的相等性是关于值相等而不是引用相等,和/或:
  2. 您有一个自定义类型,该类型具有自己的 GetHashCode 实现,该实现基于该类型的成员。

有哪位大神愿意帮我解释一下吗?


更新:感谢 Jon Skeet 回答我的问题。在其他新闻中,我做了一些挖掘并认为我想出了一个IEqualityComparer&lt;T&gt;,毕竟确实提供了身份比较!检查一下(对不起 VB.NET 的讨厌者,我刚刚创建了一个 VB.NET 项目,所以这就是我写的——翻译很简单):

Imports System.Collections.Generic
Imports System.Runtime.CompilerServices

Public Class IdentityComparer(Of T As Class)
    Implements IEqualityComparer(Of T)

    Public Overloads Function Equals(ByVal x As T, ByVal y As T) As Boolean _
        Implements IEqualityComparer(Of T).Equals

        Return Object.ReferenceEquals(x, y)
    End Function

    Public Overloads Function GetHashCode(ByVal obj As T) As Integer _
        Implements IEqualityComparer(Of T).GetHashCode

        Return RuntimeHelpers.GetHashCode(obj)
    End Function
End Class

看看这个示例程序:

Dim comparer As IEqualityComparer(Of String) = New IdentityComparer(Of String)

Dim x As New String("Hello there")
Dim y As New String("Hello there")

Console.WriteLine(comparer.Equals(x, y))
Console.WriteLine(comparer.GetHashCode(x))
Console.WriteLine(comparer.GetHashCode(y))

输出:

错误的 37121646 45592480

【问题讨论】:

    标签: .net dictionary key immutability gethashcode


    【解决方案1】:

    它不必返回唯一值。它只需要返回一个不变的 - 这就是 object.GetHashCode 所做的。

    只要DataTable 不覆盖EqualsGetHashCode,您基本上就获得了相等的对象身份——这意味着对象是否发生了变异并不重要。

    我个人希望看到IEqualityComparer&lt;T&gt; 的实现,它为 any 类型提供身份相等,但我们自己无法实现 - 无法找出什么GetHashCode 如果没有被覆盖,它会返回。 (Java 在其标准库中具有此功能,但 .NET 没有。Grr。)

    编辑:Woot - 使用object.ReferenceEqualsRuntimeHelpers.GetHashCode(),我们可以轻松实现IdentityEqualityComparer&lt;T&gt;。耶!

    【讨论】:

    • @Jon: 像 GetHashCodeBase?我承认我从未做过任何 Java(nodatime 是我的第一次尝试,我一直都在盯着代码而不是贡献)
    • @Jeff:基本上是某种静态方法。在 Java 中是 System.identityHashCode
    • @Jon:啊,听起来很有用。能够为其他目的获取 ToStringEquals 的原始实现也会很有用。
    • @Jon:嘿,看看我的更新!这和你追求的一样吗?
    【解决方案2】:

    我认为你理解的很好。对于要存储在字典中的对象,任何会影响其哈希值或测试与其他对象相等性的特征都必须是不可变的。不会影响这些事情的特征不必是不可变的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-11-25
      • 2013-12-03
      • 1970-01-01
      • 1970-01-01
      • 2012-03-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多