web-dev-qa-db-fra.com

Rendre le dictionnaire en lecture seule en C #

J'ai un Dictionary<string, List<string>> et voudrais exposer le membre en lecture seule. Je vois que je peux le renvoyer sous la forme d'un IReadOnlyDictionary<string, List<string>>, mais je ne vois pas comment le renvoyer sous la forme d'un IReadOnlyDictionary<string, IReadOnlyList<string>>.

Y a-t-il un moyen de faire cela? En c ++, j'utiliserais simplement const, mais C # ne l'a pas.

Notez que le simple fait d'utiliser IReadOnlyDictionary n'aide pas dans ce cas, car je veux aussi que les valeurs soient lues. Il semble que la seule façon de procéder consiste à créer un autre IReadOnlyDictionary et à y ajouter IReadOnlyList.

Une autre option, dont je ne serais pas ravi, serait de créer un wrapper qui implémente l'interface IReadOnlyDictionary> et de lui faire conserver une copie de l'instance d'origine, mais cela semble excessif.

6
bpeikes

Ce serait aussi simple que de transformer la référence du dictionnaire entier en IReadOnlyDictionary<string, IReadOnlyList<string>> car Dictionary<TKey, TValue> implémente IReadOnlyDictionary<TKey, TValue>.

BTW, vous ne pouvez pas faire cela parce que vous voulez que les valeurs List<string> soient IReadOnlyList<string>.

Donc vous avez besoin de quelque chose comme ça:

var readOnlyDict = (IReadOnlyDictionary<string, IReadOnlyList<string>>)dict.ToDictionary(pair => pair.Key, pair => pair.Value.AsReadOnly());

Dictionnaires immuables

Ceci est juste une suggestion, mais si vous cherchez des dictionnaires immuables, ajoutez System.Collections.Immutable package NuGet à votre solution et vous pourrez les utiliser:

// ImmutableDictionary<string, ImmutableList<string>>
var immutableDict = dict.ToImmutableDictionary(pair => pair.Key, pair => pair.Value.ToImmutableList());

En savoir plus sur Collections immuables ici .

12
Matías Fidemraizer

Étant donné que vous recherchez spécifiquement un Dictionary<string, List<string>> en lecture seule, vous recherchez essentiellement un Lookup .

L'objet Dictionnaire a une extension ToLookup().

3
John Bustos

Tout d'abord, vous devrez créer un nouveau dictionnaire avec les types de contenu souhaités:

var dicWithReadOnlyList = dic.ToDictionary(
    kv => kv.Key,
    kv => kv.Value.AsReadOnly());

Ensuite, vous pouvez simplement renvoyer le nouveau dictionnaire, puisque IReadOnlyDictionary est un supertype de Dictionary.


Pourquoi avez-vous besoin de faire ça? Parce que Dictionary<T, A> n'est pas un supertype de Dictionary<T, B>, même siA est un supertype de B. Pourquoi? Prenons l'exemple suivant:

var dic = new Dictionary<T, B>();
Dictionary<T, A> dic2 = dic;      // Imagine this were possible...

dic2.Add(someT, someA);           // ...then we'd have a type violation here, since
                                  // dic2 = dic requires some B as the value.

En d'autres termes, TValue dans Dictionaryn'est pas covariant . D'un point de vue orienté objet, la covariance devrait être possible dans la version en lecture seule du dictionnaire, mais des problèmes hérités du framework .NET empêchent cela (voir la partie commençant par "UPDATE" dans cette question pour plus de détails).

1
Heinzi

Si vous souhaitez renvoyer un dictionnaire en lecture seule tout en conservant la possibilité de le transformer en liste et en modifier la liste dans votre classe, vous pouvez utiliser la conversion pour récupérer le type de liste. 

Cet exemple est un peu artificiel, mais montre comment cela pourrait fonctionner. 

public class MyClass
{
    Dictionary<string, IReadOnlyList<string>> _dictionary;
    public IReadOnlyDictionary<string, IReadOnlyList<string>> Dictionary { get { return _dictionary; } }

    public MyClass()
    {
        _dictionary = new Dictionary<string, IReadOnlyList<string>>();
    }

    public void AddItem(string item)
    {
        IReadOnlyList<string> readOnlyList = null;
        List<string> list = null;
        if (!_dictionary.TryGetValue(item, out readOnlyList))
        {
            list = new List<string>();
            _dictionary.Add(item, list);
        }
        else
            list = readOnlyList as List<string>;
        list.Add(item);
    }
}

Si votre objectif est que la propriété soit immuable, utiliser un ReadOnlyDictionary serait la meilleure option. 

0
Patrick Huber