valor obtener new modificar llenar iterar ejemplos diccionario c# dictionary collections .net-4.5 readonly

c# - obtener - Convertir un diccionario anidado a IReadOnlyDictionary



new dictionary c# (3)

El problema para la conversión fallida es KeyValuePair: aunque clase derivada hereda la clase Base, KeyValuePair no es una subclase de KeyValuePair; ver definiciones ( Dictionary , IReadOnlyDictionary ).

Por lo tanto, siempre necesitará algún tipo de solución alternativa (el enfoque de MultiMap me parece uno, también ...). Si nestedDictionary es privado, por lo que tiene control total sobre él desde su clase, puede salirse con la suya con esto:

var nestedDictionary = new Dictionary<int, IReadOnlyList<int>>(); IReadOnlyDictionary<int, IReadOnlyList<int>> nestedReadOnlyDictionaryReference = nestedDictionary;

y cada vez que modifique una lista dentro del diccionario aplicando un molde a List<int> . Otra solución fea, lo admito, pero le ahorra memoria adicional y administración de redundancia y conserva la interfaz pública (asumed ...) de IReadOnlyDictionary<int, IReadOnlyList<int>> .

Editar: solo una idea, no la he probado, pero podría funcionar: haga que su propio diccionario agregue las interfaces faltantes para poder asignarlas al diccionario de solo lectura:

public class MyDictionary : Dictionary<int, List<int>>, ICollection<KeyValuePair<int, IReadOnlyList<int>>, IEnumerable<KeyValuePair<int, IReadOnlyList<int>>, IReadOnlyCollection<KeyValuePair<int, IReadOnlyList<int>> { }

Es posible que aún haya perdido una interfaz para implementar, y es posible que deba implementar algunos miembros todavía. Si funciona, posiblemente la solución más limpia ...

Estoy tratando de dar un IReadOnly-referencias a los objetos internos de la colección. Esto funciona bien en la mayoría de los casos, pero no lo hace si quiero convertir un diccionario que contiene una colección en un IReadOnlyDictionary que contiene un IReadOnlyCollection.

Aquí un ejemplo de código:

var list = new List<int>(); IReadOnlyList<int> listReference = list; //works; var dictionary = new Dictionary<int, int>(); IReadOnlyDictionary<int, int> dictionaryReference = dictionary; //works var nestedList = new List<List<int>>(); IReadOnlyList<IReadOnlyList<int>> nestedReadOnlyListReference = nestedList; //works var nestedDictionary = new Dictionary<int, List<int>>(); //IReadOnlyDictionary<int, IReadOnlyList<int>> nestedReadOnlyDictionaryReference = nestedDictionary; //does not work, can not implicitly convert //current workaround var nestedDictionaryReferenceHelper = new Dictionary<int, IReadOnlyList<int>>(); foreach (var kvpNestedDictionary in nestedDictionary) { nestedDictionaryReferenceHelper.Add(kvpNestedDictionary.Key, (IReadOnlyList<int>)kvpNestedDictionary.Value); } IReadOnlyDictionary<int, IReadOnlyList<int>> nestedReadOnlyDictionaryReference = nestedDictionaryReferenceHelper; //works, but is only a reference to the internal List, not to the dictionary itself

La solución es bastante fea, ya que necesita memoria adicional y necesita actualización manual cada vez que cambian los valores de nestedDictionary .

¿Hay alguna forma simple de convertir dichos diccionarios anidados?


En mi opinión, el punto es que te estás perdiendo la oportunidad de presentar un nuevo tipo apropiado con su propia dignidad. Si está utilizando Dictionary<int, List<int>> entonces se verá con un código como este cada vez que necesite insertar un valor:

if (!_dictionary.ContainsKey(key)) { var list = new List<int>(); list.Add(value); _dictionary.Add(key, list); } else { _dictionary[key].Add(value); }

Y lo que es peor con un código como este cuando desea buscar un valor:

_dictionary.ContainsKey(key) && _dictionary[key].Contains(value);

Y la variación de esos ejemplos. Lo que es peor es que expongas este detalle de implementación a los usuarios de tu clase. Si este detalle cambiará, romperá todo el código. ¿Qué HashSet<int> , por ejemplo, si desea reemplazar List<int> con HashSet<int> ?

¿Como debería ser?

_multimap.Add(key, value);

Con una interfaz adecuada (aquí muestro solo algunos métodos):

public interface IMultiMap<TKey, TValue> { void Add(TKey key, TValue value); bool ContainsKey(TKey key); }

Y su implementación:

public sealed class MultiMap<TKey, TValue> : IMultiMap<TKey, TValue> { // ... private Dictionary<int, List<int>> _items; }

Puede introducir IReadOnlyMultiMap<TKey, TValue> :

public interface IReadOnlyMultiMap<TKey, TValue> { bool ContainsKey(TKey key); }

Simplemente implemente IReadOnlyMultiMap<TKey, TValue> en MultiMap<TKey, TValue> y para devolver una colección de solo lectura, no tiene nada que hacer (ejemplo ficticio):

IReadOnlyMultiMap<int, int> MakeReadOnly(MultiMap<int, int> map) { return map; // Nothing to do! }

Tenga en cuenta que puede desear introducir una nueva ReadOnlyMultiMap<TKey, TValue> para tunelizar las llamadas de lectura a la colección en vivo subyacente (para evitar que las personas que llaman simplemente emitan a MultiMap<TKey, TValue> para eludir la limitación de solo lectura). Prueba de concepto:

public sealed class ReadOnlyMultiMap<TKey, TValue> : IReadOnlyMultiMap<TKey, TValue> { public ReadOnlyMultiMap(IMultiMap<TKey, TValue> collection) { _collection = collection; } public bool ContainsKey(TKey key) { return _collection.ContainsKey(key); } private readonly IMultiMap<TKey, TValue> _collection; }

Para devolver una vista de solo lectura, haga lo siguiente:

IReadOnlyMultiMap<int, int> MakeReadOnly(MultiMap<int, int> map) { return new ReadOnlyMultiMap<int, int>(map); }

Tenga en cuenta que hablé sobre detalles de implementación . Todavía está exponiendo un detalle de implementación (está usando una multimapa) y, si dicho código es para una API pública, debe introducir un tipo nuevo (adecuadamente nombrado) para describir lo que contiene, no cómo se implementa el almacenamiento . Puede ser MeasureCollection , SoccerScoreCollection o lo que sea que esté hablando su modelo, el almacenamiento puede variar pero el contenido no.


En esta pregunta SO , puede encontrar una muy buena explicación de por qué no se admite el lanzamiento de valores de diccionario. Por favor, vea la respuesta aceptada de Eric Lippert.

Aunque no lo recomendaría , podría usar la siguiente expresión LINQ para convertir los valores del diccionario a una lista de solo lectura:

IReadOnlyDictionary<int, IReadOnlyList<int>> nestedReadOnlyDictionaryReference = nestedDictionary.ToDictionary(kv => kv.Key, kv => kv.Value as IReadOnlyList<int>);

Es una versión más corta de su solución alternativa y se evalúa de forma diferida, pero no lo recomendaría debido a los siguientes motivos:

  1. Esta solución aún crea una copia del diccionario y no actualiza ninguna entrada nueva / eliminada del diccionario original.
  2. Los valores del diccionario, es decir, las listas de solo lectura, hacen referencia a las listas originales y los cambios allí también se actualizan en las versiones de solo lectura del diccionario.

¡Esto es un comportamiento inconsistente y, por lo tanto, una mala práctica!

A menos que no sea posible emitir los valores de un diccionario, no recomendaría hacer esto. Debería copiar en profundidad el diccionario completo, incluidas las listas anidadas, o utilizar otro contenedor que admita la conversión.