将可变对象用作词典中的键是完全正确的吗?

时间:2010-06-25 13:00:52

标签: .net dictionary key immutability gethashcode

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

一位同事建议我可以缓存我的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<DataTable, TValue>似乎能够始终为ContainsKey返回正确的值。

我想知道的是,默认情况下object.GetHashCode的基本版本是否会为每个单独的对象返回一个不变的值,或者我在DataTable看到的只是一种幻觉?< / p>

如果前者是真的 - 并且object.GetHashCode工作正常 - 似乎“仅使用不可变类型作为键”建议实际上仅适用于以下情况:

  1. 您希望对象的等式与值相等而不是引用相等,和/或:
  2. 您的自定义类型具有基于类型成员的GetHashCode实现。
  3. 那里的任何一位圣贤都在为我揭示这一点?


    更新:感谢Jon Skeet回答我的问题。在其他新闻中,我做了一些挖掘,并认为我想出了一个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
    

    看看这个示例程序:

    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 个答案:

答案 0 :(得分:2)

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

只要DataTable不覆盖EqualsGetHashCode,您基本上就会将对象标识视为相等 - 这意味着对象是否发生变异无关紧要。

就我个人而言,我希望看到IEqualityComparer<T>的实现为任何类型提供身份相同,但我们无法自己实现 - 无法找到如果没有被覆盖,那么GetHashCode 返回的内容。 (Java在其标准库中具有此功能,但.NET没有.Grr。)

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

答案 1 :(得分:0)

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