¿Está totalmente bien utilizar un objeto mutable como una clave en un diccionario?
-
29-09-2019 - |
Pregunta
decir que tengo alguna clase especial, WrappedDataTable
, y quiero asociar cada WrappedDataTable
con exactamente un DataTable
. Por otra parte, yo quiero que haya más de un WrappedDataTable
en existencia para cualquier DataTable
dado.
Un colega me sugirió que podía almacenar en caché mi WrappedDataTable
y utilizar un método de fábrica para acceder a uno, como esto:
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;
}
}
Esto me pareció muy cuestionable en un primer momento, supongo que porque me he convertido en familiarizados con la idea de que las llaves en un diccionario deben ser tipos inmutables. Pero tal vez esto no es necesariamente el caso? Una prueba rápida me reveló que un DataTable
parece mantener un código hash consistente a lo largo de numerosas modificaciones a su contenido; por lo tanto, un Dictionary<DataTable, TValue>
parece ser capaz de devolver un valor correcto para ContainsKey
consistente.
Lo que me pregunto es si la versión base del object.GetHashCode
por defecto devolverá un valor que no cambia para cada objeto individual, o si lo que estoy viendo con DataTable
es sólo una ilusión?
Si el primero es cierto - y object.GetHashCode
funciona bien - parece que el "uso únicos tipos inmutables como teclas de" asesoramiento en realidad sólo se aplica a los escenarios en los que:
- ¿Quieres la igualdad de los objetos que se acerca la igualdad de valor en lugar de la igualdad de referencia, y / o:
- Usted tiene un tipo personalizado con su propia aplicación
GetHashCode
que se basa en los miembros del tipo.
Cualquier sabios por ahí para el cuidado de arrojar algo de luz sobre esto para mí?
Actualizar : Gracias a Jon Skeet por responder a mi pregunta. En otras noticias, he hecho un poco de excavación y creo que se me ocurrió una IEqualityComparer<T>
que hace proporcionar la comparación de identidad después de todo! Comprobar que funciona (lo siento enemigos VB.NET, acabo de tener un proyecto VB.NET de modo que es lo que escribió en - traducción es 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
Tome un vistazo a este programa de ejemplo:
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))
Salida:
False 37121646 45592480
Solución
No tiene que devolver un valor único. Sólo tiene que devolver un uno que no cambia -. Y eso es lo que hace object.GetHashCode
la identidad del objeto Mientras DataTable
no anula Equals
o GetHashCode
, que ha conseguido básicamente como la igualdad -. Lo que significa que no importa si el objeto está mutado
Personalmente me gustaría ver una implementación de IEqualityComparer<T>
que proporciona igualdad de identidad para cualquier tipo, pero no podemos aplicar a nosotros mismos de que - no hay manera de saber lo que GetHashCode
< em> le han regresado si no hubiera sido anulado. (Java tiene esta capacidad en sus bibliotecas estándar, pero .NET no. Grr.)
EDIT: Woot - con object.ReferenceEquals
y RuntimeHelpers.GetHashCode()
, se puede implementar fácilmente un IdentityEqualityComparer<T>
. Yay!
Otros consejos
Creo que entienden las cosas bien. Para que un objeto se almacena en un diccionario, cualquier característica que pueda modificar su valor hash o pruebas de igualdad con otros objetos deben ser inmutables. Características que no afectarían esas cosas no tienen por qué ser inmutable.