Frage

Ist in den C#-Kernbibliotheken irgendetwas integriert, das mir ein unveränderliches Wörterbuch geben kann?

Etwas in der Art von Javas:

Collections.unmodifiableMap(myMap);

Und nur um es klarzustellen: Ich möchte nicht verhindern, dass die Schlüssel/Werte selbst geändert werden, sondern nur die Struktur des Wörterbuchs.Ich möchte etwas, das schnell und laut fehlschlägt, wenn eine der Mutatormethoden von IDictionary aufgerufen wird (Add, Remove, Clear).

War es hilfreich?

Lösung

Nein, aber ein Wrapper ist eher 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();
    }
}

Natürlich können Sie den Setter this[] oben ändern, wenn Sie das Ändern von Werten zulassen möchten.

Andere Tipps

Mit der Veröffentlichung von .NET 4.5 gibt es eine neue ReadOnlyDictionary Klasse.Sie übergeben einfach eine IDictionary an den Konstruktor, um das unveränderliche Wörterbuch zu erstellen.

Hier ist eine hilfreiche Erweiterungsmethode, die zur Vereinfachung der Erstellung des schreibgeschützten Wörterbuchs verwendet werden kann.

Das glaube ich nicht.Es gibt eine Möglichkeit, eine schreibgeschützte Liste und eine schreibgeschützte Sammlung zu erstellen, aber ich glaube nicht, dass es ein integriertes schreibgeschütztes Wörterbuch gibt.System.ServiceModel verfügt über eine ReadOnlyDictinoary-Implementierung, diese ist jedoch intern.Wahrscheinlich wäre es jedoch nicht allzu schwer, es mit Reflector zu kopieren oder einfach ein eigenes von Grund auf zu erstellen.Es umschließt grundsätzlich ein Wörterbuch und löst aus, wenn ein Mutator aufgerufen wird.

Hinzufügen zu dbkks Antwort, wollte ich beim ersten Erstellen meines ReadOnlyDictionary einen Objektinitialisierer verwenden können.Ich habe folgende Änderungen vorgenommen:

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"
        );
    }
}

Jetzt kann ich die Klasse folgendermaßen verwenden:

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

Die Open-Source PowerCollections Die Bibliothek enthält einen schreibgeschützten Wörterbuch-Wrapper (sowie schreibgeschützte Wrapper für so ziemlich alles andere), auf den über eine statische Datei zugegriffen werden kann ReadOnly() Methode auf der Algorithms Klasse.

Eine Problemumgehung könnte darin bestehen, eine neue Liste von KeyValuePair aus dem Wörterbuch zu erstellen, um das Original unverändert zu lassen.

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;

Auf diese Weise wird das ursprüngliche Wörterbuch nicht geändert.

„Out of the box“ gibt es keine Möglichkeit, dies zu tun.Sie können eine erstellen, indem Sie Ihre eigene Dictionary-Klasse ableiten und die erforderlichen Einschränkungen implementieren.

Ich habe hier eine Implementierung einer unveränderlichen (nicht READONLY) Implementierung eines AVLTree für C# gefunden.

Ein AVL-Baum hat logarithmische (nicht konstante) Kosten für jede Operation, ist aber immer noch schnell.

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

Seit Linq gibt es eine generische Schnittstelle Ich schlage nach.Lesen Sie mehr in MSDN.

Um einfach ein unveränderliches Wörterbuch zu erhalten, können Sie daher Folgendes aufrufen:

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

Sie könnten so etwas versuchen:

private readonly Dictionary<string, string> _someDictionary;

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

Dies würde das Veränderlichkeitsproblem beseitigen und Ihr Anrufer müsste es entweder in sein eigenes Wörterbuch konvertieren:

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

...oder verwenden Sie eine Vergleichsoperation für den Schlüssel anstelle einer Indexsuche, z. B.:

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

Im Allgemeinen ist es eine viel bessere Idee, überhaupt keine Wörterbücher weiterzugeben (wenn Sie es nicht MÜSSEN).

Erstellen Sie stattdessen ein Domänenobjekt mit einer Schnittstelle, die keine Methoden anbietet modifizieren das Wörterbuch (das es umschließt).Stattdessen wird die erforderliche LookUp-Methode angeboten, die Elemente anhand des Schlüssels aus dem Wörterbuch abruft (der Bonus ist, dass es auch einfacher zu verwenden ist als ein Wörterbuch).

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...};
}

Es gibt auch eine andere Alternative, wie ich beschrieben habe:

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

Im Wesentlichen handelt es sich um eine Unterklasse von ReadOnlyCollection>, die die Arbeit auf elegantere Weise erledigt.Elegant in dem Sinne, dass es zur Kompilierungszeit Unterstützung bietet, um das Wörterbuch schreibgeschützt zu machen, anstatt Ausnahmen von Methoden auszulösen, die die darin enthaltenen Elemente ändern.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top