Estoy devolviendo una referencia a un diccionario en mi propiedad de solo lectura. ¿Cómo evito que los consumidores cambien mis datos? Si esto fuera un IList
, simplemente podría devolverlo AsReadOnly
. ¿Hay algo similar que pueda hacer con un diccionario?
Private _mydictionary As Dictionary(Of String, String)
Public ReadOnly Property MyDictionary() As Dictionary(Of String, String)
Get
Return _mydictionary
End Get
End Property
.net
dictionary
readonly
Rob Sobers
fuente
fuente
Respuestas:
Aquí hay una implementación simple que envuelve un diccionario:
fuente
.NET 4.5
El .NET Framework 4.5 BCL presenta
ReadOnlyDictionary<TKey, TValue>
( fuente ).Como .NET Framework 4.5 BCL no incluye un
AsReadOnly
diccionario, deberá escribir el suyo propio (si lo desea). Sería algo como lo siguiente, cuya simplicidad quizás resalta por qué no era una prioridad para .NET 4.5..NET 4.0 y menos
Antes de .NET 4.5, no existe una clase de .NET Framework que se ajusta un
Dictionary<TKey, TValue>
como el ReadOnlyCollection envuelve una lista . Sin embargo, no es difícil crear uno.Aquí hay un ejemplo : hay muchos otros si busca en Google ReadOnlyDictionary .
fuente
AsReadOnly()
método de la forma habitualDictionary<,>
, así que me pregunto cuántas personas descubrirán su nuevo tipo. Sin embargo, este hilo de desbordamiento de pila ayudará.En la reciente conferencia BUILD se anunció que, desde .NET 4.5, la interfaz
System.Collections.Generic.IReadOnlyDictionary<TKey,TValue>
está incluida. La prueba está aquí (Mono) y aquí (Microsoft);)No estoy seguro si también
ReadOnlyDictionary
está incluido, pero al menos con la interfaz no debería ser difícil crear ahora una implementación que exponga una interfaz genérica .NET oficial :)fuente
ReadOnlyDictionary<TKey, TValue>
(.Net 4.5) - msdn.microsoft.com/en-us/library/gg712875.aspxSiéntase libre de usar mi envoltura simple. NO implementa IDictionary, por lo que no tiene que lanzar excepciones en tiempo de ejecución para los métodos de diccionario que cambiarían el diccionario. Los métodos de cambio simplemente no están ahí. Hice mi propia interfaz para ello llamada IReadOnlyDictionary.
fuente
IDictionary
contrato. Creo que es más correcto desde una perspectiva OOPIDictionary
heredar deIReadOnlyDictionary
.IDictionary
(para el actualIReadOnlyDictionary
) yIMutableDictionary
(para el actualIDictionary
).IsReadOnly on
IDictionary<TKey,TValue>
se hereda deICollection<T>
(seIDictionary<TKey,TValue>
extiendeICollection<T>
comoICollection<KeyValuePair<TKey,TValue>>
). No se usa ni implementa de ninguna manera (y de hecho está "oculto" mediante el uso de la implementación explícita de losICollection<T>
miembros asociados ).Hay al menos 3 formas en que puedo ver para resolver el problema:
IDictionary<TKey, TValue>
y ajuste / delegue a un diccionario interno como se ha sugeridoICollection<KeyValuePair<TKey, TValue>>
conjunto como de solo lectura oIEnumerable<KeyValuePair<TKey, TValue>>
dependiendo del uso del valor.ctor(IDictionary<TKey, TValue>)
y devuelva una copia, de esa manera el usuario puede hacerlo libremente, y no afecta el estado del objeto que aloja el diccionario fuente. Tenga en cuenta que si el diccionario que está clonando contiene tipos de referencia (no cadenas como se muestra en el ejemplo), tendrá que hacer la copia "manualmente" y clonar también los tipos de referencia.Como un aparte; al exponer colecciones, intente exponer la interfaz más pequeña posible; en el caso de ejemplo, debe ser IDictionary, ya que le permite variar la implementación subyacente sin romper el contrato público que expone el tipo.
fuente
Un diccionario de solo lectura puede ser reemplazado hasta cierto punto por
Func<TKey, TValue>
: por lo general, lo uso en una API si solo quiero que las personas realicen búsquedas; es simple, y en particular, es simple reemplazar el backend si alguna vez lo desea. Sin embargo, no proporciona la lista de claves; si eso importa depende de lo que estés haciendo.fuente
No, pero sería fácil rodar el tuyo. IDictionary define una propiedad IsReadOnly. Simplemente envuelva un diccionario y arroje una excepción NotSupportedException de los métodos apropiados.
fuente
Ninguno disponible en el BCL. Sin embargo, publiqué un ReadOnlyDictionary (llamado ImmutableMap) en mi proyecto BCL Extras
Además de ser un diccionario totalmente inmutable, admite la producción de un objeto proxy que implementa IDictionary y puede usarse en cualquier lugar donde se tome IDictionary. Lanzará una excepción cada vez que se llame a una de las API mutantes
fuente
Puede crear una clase que solo implemente una implementación parcial del diccionario y oculte todas las funciones de agregar / quitar / establecer.
Use un diccionario internamente al que la clase externa pasa todas las solicitudes.
Sin embargo, dado que es probable que su diccionario contenga tipos de referencia, no hay forma de evitar que el usuario establezca valores en las clases que posee el diccionario (a menos que esas clases sean solo de lectura)
fuente
No creo que haya una manera fácil de hacerlo ... si su diccionario es parte de una clase personalizada, podría lograrlo con un indexador:
fuente
+1 Buen trabajo, Thomas. Llevé a ReadOnlyDictionary un paso más allá.
Al igual que la solución de Dale, que quería eliminar
Add()
,Clear()
,Remove()
,, etc de IntelliSense. Pero quería que mis objetos derivados se implementaranIDictionary<TKey, TValue>
.Además, me gustaría que se rompa el siguiente código: (Nuevamente, la solución de Dale también lo hace)
La línea Add () da como resultado:
La persona que llama aún puede enviarlo
IDictionary<TKey, TValue>
, pero seNotSupportedException
generará si intenta usar los miembros que no son de solo lectura (de la solución de Thomas).De todos modos, aquí está mi solución para cualquiera que también quisiera esto:
fuente
Ahora, hay colecciones inmutables de Microsoft (
System.Collections.Immutable
). Consíguelos a través de NuGet .fuente
fuente
public IEnumerable<KeyValuePair<string, string>> MyDictionary() { return _mydictionary; }
Esta es una mala solución, ver abajo.
Para aquellos que todavía usan .NET 4.0 o anterior, tengo una clase que funciona igual que la de la respuesta aceptada, pero es mucho más corta. Extiende el objeto Diccionario existente, anula (en realidad oculta) ciertos miembros para que arrojen una excepción cuando se les llama.
Si la persona que llama intenta llamar a Agregar, Eliminar u otra operación de mutación que tiene el Diccionario incorporado, el compilador arrojará un error. Uso los atributos Obsoletos para generar estos errores de compilación. De esta manera, puede reemplazar un diccionario con este ReadOnlyDictionary e inmediatamente ver dónde podrían estar los problemas sin tener que ejecutar su aplicación y esperar excepciones de tiempo de ejecución.
Echar un vistazo:
Esta solución tiene un problema señalado por @supercat ilustrado aquí:
En lugar de dar un error en tiempo de compilación como esperaba, o una excepción en tiempo de ejecución como esperaba, este código se ejecuta sin error. Imprime cuatro números. Eso hace que mi ReadOnlyDictionary sea un ReadWriteDictionary.
fuente
Dictionary<TKey,TValue>
sin ninguna queja del compilador, y emitir o coaccionar una referencia al tipo de diccionario simple eliminará cualquier protección.Dictionary
con unClone
método que encadenado aMemberwiseClone
. Desafortunadamente, si bien debería ser posible clonar eficientemente un diccionario clonando los almacenes de respaldo, el hecho de que los almacenes de respaldo seanprivate
más queprotected
medios significa que no hay forma de que una clase derivada los clone; usarMemberwiseClone
sin clonar los almacenes de respaldo significará que las modificaciones posteriores realizadas al diccionario original romperán el clon, y las modificaciones realizadas al clon romperán el original.