Search code examples
c#dictionarycollections.net-4.5readonly

Converting a nested dictionary to IReadOnlyDictionary


I am trying to give out a IReadOnly-references to internal Collection objects. This works well in most cases, but does not if i want to convert a dictionary containing a collection into an IReadOnlyDictionary containing a IReadOnlyCollection.

Here a code example:

    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

The workaround is pretty ugly as it needs additional memory and needs manual updating every time the values of nestedDictionary change.

Is there any simple way to convert such nested dictionaries?


Solution

  • In this SO question you can find a very good explanation why casting dictionary values is not supported. Please see the accepted answer of Eric Lippert.

    Although i would not recommend this, you could use the following LINQ expression to cast the values of the dictionary to a read only list:

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

    It is a shorter version of your workaround and it is lazy evaluated, but i would not recommend this due to the following reasons:

    1. This solution still creates a copy of the dictionary and does not update any new/deleted entries from the original dictionary.
    2. The values of the dictionary, i.e. the readonly lists, refer to the original lists and changes there are updated in the read only versions in the dictionary too.

    This is inconsistent behavior and therefore a bad practice!

    Unless it is not possible to cast the values of a dictionary, i would not recommend doing this. You should either deep copy the entire dictionary including the nested lists, or use an other container that supports casting.