Is it totally fine to use a mutable object as a key in a Dictionary?
Say I have some special class, WrappedDataTable
, and I want to associate each WrappedDataTable
with exactly one DataTable
. Furthermore, I want there to be no more than one WrappedDataTable
in existence for any given DataTable
.
A colleague suggested I could cache my WrappedDataTable
and use a factory method to access one, like this:
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;
}
}
This struck me as very questionable at first, I guess because I've become familiar with the idea that keys in a dictionary should be immutable types. But perhaps this is not necessarily the case? A quick test revealed to me that a DataTable
appears to maintain a consistent hash code over the course of numerous modifications to its contents; a Dictionary<DataTable, TValue>
therefore appears to be able to return a correct value for ContainsKey
consistently.
What I'm wondering is if the base version of object.GetHashCode
by default will return an unchanging value for every individual object, or if what I'm seeing with DataTable
is just an illusion?
If the former is true -- and object.GetHashCode
works just fine -- it seems the "use only immutable types as keys" advice really only applies to scenarios where:
- You want equality of objects to be about value equality as opposed to reference equality, and/or:
- You have a custom type with its own
GetHashCode
implementation that is based on the type's members.
Any sages out there care to shed some light on this for me?
UPDATE: Thanks to Jon Skeet for answering my question. In other news, I开发者_如何学编程 did some digging and think I came up with an IEqualityComparer<T>
that does provide identity comparison after all! Check it out (sorry VB.NET haters, I just had a VB.NET project up so that's what I wrote it in -- translation is trivial):
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))
Output:
False 37121646 45592480
It doesn't have to return a unique value. It just has to return an unchanging one - and that's what object.GetHashCode
does.
So long as DataTable
doesn't override Equals
or GetHashCode
, you've basically got object identity as equality - which means it doesn't matter if the object is mutated.
Personally I'd like to see an implementation of IEqualityComparer<T>
which provides identity equality for any type, but we can't implement that ourselves - there's no way of finding out what GetHashCode
would have returned if it hadn't been overridden. (Java has this capability in its standard libraries, but .NET doesn't. Grr.)
EDIT: Woot - with object.ReferenceEquals
and RuntimeHelpers.GetHashCode()
, we can easily implement an IdentityEqualityComparer<T>
. Yay!
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.
精彩评论