代码之家  ›  专栏  ›  技术社区  ›  Dan Tao

在字典中使用可变对象作为关键字是否完全正确?

  •  1
  • Dan Tao  · 技术社区  · 15 年前

    比如说我有一些特殊的课程, WrappedDataTable 我想把每一个 包装数据表 恰好一 DataTable . 此外,我希望有不止一个 可擦除数据的 存在于任何给定的 可计算的 .

    A colleague suggested I could cache my 包装数据表 并使用工厂方法访问一个,如下所示:

    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;
        }
    }
    

    一开始我觉得这很可疑,因为我已经熟悉字典中的键应该是不可变的类型。但也许情况并非如此?一个快速的测试告诉我 可计算的 在对其内容进行多次修改的过程中,似乎保持了一致的哈希代码;a Dictionary<DataTable, TValue> 因此,似乎能够返回正确的值。 ContainsKey 一贯地。

    我想知道的是基础版本 object.GetHashCode 默认情况下,将返回每个对象的不变值,或者如果我看到的是 可计算的 只是一种幻觉?

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

    1. You want equality of objects to be about value equality as opposed to reference equality, and/or:
    2. 您有自己的自定义类型 GetHashCode 基于类型成员的实现。

    外面有什么圣人关心我吗?


    更新 感谢乔恩·斯基特回答我的问题。在另一个新闻里,我做了一些挖掘,认为我想出了一个 IEqualityComparer<T> 那个 毕竟提供身份比较!检查一下(抱歉,我讨厌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
    

    Take a look at this example program:

    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))
    

    输出:

    False
    37121646
    45592480
    
    2 回复  |  直到 15 年前
        1
  •  2
  •   Jon Skeet    15 年前

    It doesn't have to return a unique value. It just has to return an unchanging one - and that's what object.GetHashCode 做。

    只要 DataTable 不重写 Equals GetHashCode , you've basically got object identity as equality - which means it doesn't matter if the object is mutated.

    就我个人而言,我希望看到一个 IEqualityComparer<T> which provides identity equality for 任何 方法 如果它没有被重写,则返回。(Java在它的标准库中具有这种能力,但是.NET没有.GRR)。

    编辑:Woot-with object.ReferenceEquals RuntimeHelpers.GetHashCode() IdentityEqualityComparer<T> . 哎呀!

        2
  •  0
  •   supercat    15 年前

    I think you understand things fine. For an object to be stored in a dictionary, any characteristics which would affect its hash value or tests for equality with other objects must be immutable. Characteristics which would not affect those things need not be immutable.

    推荐文章