¿Tiene C # una manera de darme un diccionario inmutable?

¿Hay algo incorporado en las bibliotecas centrales de C # que pueda darme un diccionario inmutable?

Algo parecido a Java :

Collections.unmodifiableMap(myMap); 

Y solo para aclarar, no estoy buscando detener el cambio de las claves / valores mismos, solo la estructura del Diccionario. Quiero algo que falle rápido y fuerte si se llama a cualquiera de los métodos del mutador de IDictionary ( Add, Remove, Clear ).

No, pero un envoltorio es bastante trivial:

 public class ReadOnlyDictionary : IDictionary { IDictionary _dict; public ReadOnlyDictionary(IDictionary backingDict) { _dict = backingDict; } public void Add(TKey key, TValue value) { throw new InvalidOperationException(); } public bool ContainsKey(TKey key) { return _dict.ContainsKey(key); } public ICollection Keys { get { return _dict.Keys; } } public bool Remove(TKey key) { throw new InvalidOperationException(); } public bool TryGetValue(TKey key, out TValue value) { return _dict.TryGetValue(key, out value); } public ICollection Values { get { return _dict.Values; } } public TValue this[TKey key] { get { return _dict[key]; } set { throw new InvalidOperationException(); } } public void Add(KeyValuePair item) { throw new InvalidOperationException(); } public void Clear() { throw new InvalidOperationException(); } public bool Contains(KeyValuePair item) { return _dict.Contains(item); } public void CopyTo(KeyValuePair[] array, int arrayIndex) { _dict.CopyTo(array, arrayIndex); } public int Count { get { return _dict.Count; } } public bool IsReadOnly { get { return true; } } public bool Remove(KeyValuePair item) { throw new InvalidOperationException(); } public IEnumerator> GetEnumerator() { return _dict.GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return ((System.Collections.IEnumerable)_dict).GetEnumerator(); } } 

Obviamente, puede cambiar el setter anterior [] si desea permitir la modificación de valores.

Hasta donde yo sé, no hay. Pero puede ser que pueda copiar algún código (y aprender mucho) de estos artículos:

Inmutabilidad en C # primera parte: tipos de inmutabilidad
Inmutabilidad en C # Parte dos: Una stack inmutable simple
Inmutabilidad en C # parte tres: una stack inmutable covariante
Inmutabilidad en C # Parte cuatro: Una cola inmutable
Inmutabilidad en C # parte seis: un árbol binario simple
Inmutabilidad en C # Parte Siete: Más en árboles binarios
Inmutabilidad en C # parte ocho: aún más en árboles binarios
Inmutabilidad en C # Parte Nueve: ¿Académico? Además de mi implementación de árbol AVL
Inmutabilidad en C # Parte 10: Una cola de doble extremo
Inmutabilidad en C # Parte Once: Una cola de doble extremo en funcionamiento

Con el lanzamiento de .NET 4.5, hay una nueva clase ReadOnlyDictionary . Simplemente IDictionary un IDictionary al constructor para crear el diccionario inmutable.

Aquí hay un método de extensión útil que se puede utilizar para simplificar la creación del diccionario de solo lectura.

No lo creo. Hay una manera de crear una lista de solo lectura y una colección de solo lectura, pero no creo que haya un diccionario incorporado de solo lectura. System.ServiceModel tiene una implementación ReadOnlyDictinoary, pero es interna. Sin embargo, probablemente no sería demasiado difícil copiarlo, utilizando Reflector, o simplemente crear uno propio desde cero. Básicamente envuelve un diccionario y lanza cuando se llama un mutador.

Añadiendo a la respuesta de dbkk , quería poder usar un inicializador de objetos cuando primero creaba mi ReadOnlyDictionary. Hice las siguientes modificaciones:

 private readonly int _finalCount; ///  /// Takes a count of how many key-value pairs should be allowed. /// Dictionary can be modified to add up to that many pairs, but no /// pair can be modified or removed after it is added. Intended to be /// used with an object initializer. ///  ///  public ReadOnlyDictionary(int count) { _dict = new SortedDictionary(); _finalCount = count; } ///  /// To allow object initializers, this will allow the dictionary to be /// added onto up to a certain number, specifically the count set in /// one of the constructors. ///  ///  ///  public void Add(TKey key, TValue value) { if (_dict.Keys.Count < _finalCount) { _dict.Add(key, value); } else { throw new InvalidOperationException( "Cannot add pair <" + key + ", " + value + "> because " + "maximum final count " + _finalCount + " has been reached" ); } } 

Ahora puedo usar la clase así:

 ReadOnlyDictionary Fields = new ReadOnlyDictionary(2) { {"hey", "now"}, {"you", "there"} }; 

La biblioteca de código abierto PowerCollections incluye un contenedor de diccionario de solo lectura (así como contenedores de solo lectura para casi todo lo demás), accesible a través de un método ReadOnly() estático ReadOnly() en la clase Algorithms .

Una solución podría ser, lanzar una nueva lista de KeyValuePair del Diccionario para mantener el original sin modificaciones.

 var dict = new Dictionary(); dict.Add("Hello", "World"); dict.Add("The", "Quick"); dict.Add("Brown", "Fox"); var dictCopy = dict.Select( item => new KeyValuePair(item.Key, item.Value)); // returns dictCopy; 

De esta forma, el diccionario original no se modificará.

“Fuera de la caja” no hay una manera de hacer esto. Puede crear uno derivando su propia clase de diccionario e implementando las restricciones que necesita.

He encontrado una implementación de una implementación Inmutable (no READONLY) de un AVLTree para C # aquí.

Un árbol AVL tiene un costo logarítmico (no constante) en cada operación, pero aún así rápido.

http://csharpfeeds.com/post/7512/Immutability_in_Csharp_Part_Nine_Academic_Plus_my_AVL_tree_implementation.aspx

Desde Linq, hay una interfaz genérica ILookup . Lea más en MSDN .

Por lo tanto, para obtener simplemente un diccionario inmutable, puede llamar a:

 using System.Linq; // (...) var dictionary = new Dictionary(); // (...) var read_only = dictionary.ToLookup(kv => kv.Key, kv => kv.Value); 

Podrías probar algo como esto:

 private readonly Dictionary _someDictionary; public IEnumerable> SomeDictionary { get { return _someDictionary; } } 

Esto eliminaría el problema de mutabilidad a favor de que tu interlocutor deba convertirlo a su propio diccionario:

 foo.SomeDictionary.ToDictionary(kvp => kvp.Key); 

… o utilice una operación de comparación en la clave en lugar de una búsqueda de índice, por ejemplo:

 foo.SomeDictionary.First(kvp => kvp.Key == "SomeKey"); 

En general, es una idea mucho mejor no pasar ningún diccionario en primer lugar (si no TIENE que hacerlo).

En su lugar, cree un objeto de dominio con una interfaz que no ofrezca ningún método que modifique el diccionario (que se ajusta). En cambio, ofrece el método LookUp requerido que recupera elementos del diccionario por clave (además, es más fácil de usar que un diccionario).

 public interface IMyDomainObjectDictionary { IMyDomainObject GetMyDomainObject(string key); } internal class MyDomainObjectDictionary : IMyDomainObjectDictionary { public IDictionary _myDictionary { get; set; } public IMyDomainObject GetMyDomainObject(string key) {.._myDictionary .TryGetValue..etc...}; } 

También hay otra alternativa como la que describí en:

http://www.softwarerockstar.com/2010/10/readonlydictionary-tkey-tvalue/

Esencialmente es una subclase de ReadOnlyCollection>, que hace el trabajo de una manera más elegante. Elegante en el sentido de que tiene soporte en tiempo de comstackción para hacer que el diccionario sea de solo lectura en lugar de arrojar excepciones desde los métodos que modifican los elementos dentro de él.