Question

Existe-t-il quelque chose d'intégré aux bibliothèques principales de C# qui puisse me donner un dictionnaire immuable ?

Quelque chose dans le sens de Java:

Collections.unmodifiableMap(myMap);

Et juste pour clarifier, je ne cherche pas à empêcher la modification des clés/valeurs elles-mêmes, mais simplement de la structure du dictionnaire.Je veux quelque chose qui échoue rapidement et fort si l'une des méthodes de mutation d'IDictionary est appelée (Add, Remove, Clear).

Était-ce utile?

La solution

Non, mais un wrapper, c'est plutôt trivial :

public class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
    IDictionary<TKey, TValue> _dict;

    public ReadOnlyDictionary(IDictionary<TKey, TValue> backingDict)
    {
        _dict = backingDict;
    }

    public void Add(TKey key, TValue value)
    {
        throw new InvalidOperationException();
    }

    public bool ContainsKey(TKey key)
    {
        return _dict.ContainsKey(key);
    }

    public ICollection<TKey> 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<TValue> Values
    {
        get { return _dict.Values; }
    }

    public TValue this[TKey key]
    {
        get { return _dict[key]; }
        set { throw new InvalidOperationException(); }
    }

    public void Add(KeyValuePair<TKey, TValue> item)
    {
        throw new InvalidOperationException();
    }

    public void Clear()
    {
        throw new InvalidOperationException();
    }

    public bool Contains(KeyValuePair<TKey, TValue> item)
    {
        return _dict.Contains(item);
    }

    public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
    {
        _dict.CopyTo(array, arrayIndex);
    }

    public int Count
    {
        get { return _dict.Count; }
    }

    public bool IsReadOnly
    {
        get { return true; }
    }

    public bool Remove(KeyValuePair<TKey, TValue> item)
    {
        throw new InvalidOperationException();
    }

    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
    {
        return _dict.GetEnumerator();
    }

    System.Collections.IEnumerator 
           System.Collections.IEnumerable.GetEnumerator()
    {
        return ((System.Collections.IEnumerable)_dict).GetEnumerator();
    }
}

Évidemment, vous pouvez modifier le paramètre this[] ci-dessus si vous souhaitez autoriser la modification des valeurs.

Autres conseils

Avec la sortie de .NET 4.5, il existe un nouveau Dictionnaire en lecture seule classe.Vous passez simplement un IDictionary au constructeur pour créer le dictionnaire immuable.

Ici est une méthode d'extension utile qui peut être utilisée pour simplifier la création du dictionnaire en lecture seule.

Je ne pense pas.Il existe un moyen de créer une liste en lecture seule et une collection en lecture seule, mais je ne pense pas qu'il existe un dictionnaire intégré en lecture seule.System.ServiceModel a une implémentation ReadOnlyDictinoary, mais elle est interne.Il ne serait probablement pas trop difficile de le copier, en utilisant Reflector, ou simplement de créer le vôtre à partir de zéro.Il enveloppe essentiellement un dictionnaire et le lance lorsqu'un mutateur est appelé.

Ajout sur La réponse de dbkk, je voulais pouvoir utiliser un initialiseur d'objet lors de la première création de mon ReadOnlyDictionary.J'ai apporté les modifications suivantes :

private readonly int _finalCount;

/// <summary>
/// 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.
/// </summary>
/// <param name="count"></param>
public ReadOnlyDictionary(int count)
{
    _dict = new SortedDictionary<TKey, TValue>();
    _finalCount = count;
}

/// <summary>
/// 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.
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
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"
        );
    }
}

Maintenant, je peux utiliser la classe comme ceci :

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

L'open source PowerCollections La bibliothèque comprend un wrapper de dictionnaire en lecture seule (ainsi que des wrappers en lecture seule pour à peu près tout le reste), accessible via un fichier statique ReadOnly() méthode sur le Algorithms classe.

Une solution de contournement pourrait consister à lancer une nouvelle liste de KeyValuePair à partir du dictionnaire pour conserver l'original non modifié.

var dict = new Dictionary<string, string>();

dict.Add("Hello", "World");
dict.Add("The", "Quick");
dict.Add("Brown", "Fox");

var dictCopy = dict.Select(
    item => new KeyValuePair<string, string>(item.Key, item.Value));

// returns dictCopy;

De cette façon, le dictionnaire original ne sera pas modifié.

"Hors de la boîte", il n'y a aucun moyen de le faire.Vous pouvez en créer une en dérivant votre propre classe Dictionary et en implémentant les restrictions dont vous avez besoin.

J'ai trouvé ici une implémentation d'une implémentation inmuable (pas READONLY) d'un AVLTree pour C#.

Un arbre AVL a un coût logarithmique (non constant) pour chaque opération, mais reste rapide.

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

Depuis Linq, il existe une interface générique Je regarde.Lire la suite dans MSDN.

Par conséquent, pour obtenir simplement un dictionnaire immuable, vous pouvez appeler :

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

Vous pourriez essayer quelque chose comme ceci :

private readonly Dictionary<string, string> _someDictionary;

public IEnumerable<KeyValuePair<string, string>> SomeDictionary
{
    get { return _someDictionary; }
}

Cela supprimerait le problème de mutabilité et obligerait votre appelant à le convertir dans son propre dictionnaire :

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

...ou utilisez une opération de comparaison sur la clé plutôt qu'une recherche d'index, par exemple :

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

En général, il est préférable de ne pas faire circuler de dictionnaire en premier lieu (si vous n'êtes pas OBLIGATOIRE de le faire).

Au lieu de cela, créez un objet de domaine avec une interface qui n'offre aucune méthode modification le dictionnaire (qu'il enveloppe).Au lieu de cela, nous proposons la méthode LookUp requise qui récupère l'élément du dictionnaire par clé (le bonus est qu'elle le rend également plus facile à utiliser qu'un dictionnaire).

public interface IMyDomainObjectDictionary 
{
    IMyDomainObject GetMyDomainObject(string key);
}

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

Il existe également une autre alternative, comme je l'ai décrit ici :

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

Il s'agit essentiellement d'une sous-classe de ReadOnlyCollection>, qui effectue le travail de manière plus élégante.Élégant dans le sens où il prend en charge au moment de la compilation la création du dictionnaire en lecture seule plutôt que de lancer des exceptions aux méthodes qui modifient les éléments qu'il contient.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top