Gibt es ein schreibgeschütztes generisches Wörterbuch in .NET?

185

Ich gebe einen Verweis auf ein Wörterbuch in meiner schreibgeschützten Eigenschaft zurück. Wie verhindere ich, dass Verbraucher meine Daten ändern? Wenn dies ein wäre, IListkönnte ich es einfach zurückgeben AsReadOnly. Gibt es etwas Ähnliches, das ich mit einem Wörterbuch machen kann?

Private _mydictionary As Dictionary(Of String, String)
Public ReadOnly Property MyDictionary() As Dictionary(Of String, String)
    Get
        Return _mydictionary
    End Get
End Property
Rob Sobers
quelle
4
Es muss eine Möglichkeit geben, sonst würde es keine IsReadOnly-Eigenschaft im IDictionary geben ... ( msdn.microsoft.com/en-us/library/bb338949.aspx )
Powerlord
2
Viele der konzeptionellen Vorteile der Unveränderlichkeit können erzielt werden, ohne dass die Laufzeit dies erzwingt. Wenn dies ein privates Projekt ist, ziehen Sie eine disziplinierte, informelle Methode in Betracht. Wenn Sie einem Verbraucher Daten zur Verfügung stellen müssen, sollten Sie tiefgreifende Kopien ernsthaft in Betracht ziehen. Wenn Sie bedenken, dass für eine unveränderliche Auflistung 1) ein unveränderlicher Verweis auf die Auflistung erforderlich ist 2) eine Mutation der Sequenz selbst verhindert wird und 3) verhindert wird, dass die Eigenschaften der Elemente der Auflistung geändert werden, und dass einige davon durch Reflexion verletzt werden können, ist die Laufzeitdurchsetzung nicht praktisch.
Sprague
26
Seit .NET 4.5 gibt es ein System.Collections.ObjectModel.ReadOnlyDictionary ^ _ ^
Smartkid
2
Es gibt jetzt auch Microsoft Immutable Collections über NuGet msdn.microsoft.com/en-us/library/dn385366%28v=vs.110%29.aspx
VoteCoffee

Antworten:

156

Hier ist eine einfache Implementierung, die ein Wörterbuch umschließt:

public class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
    private readonly IDictionary<TKey, TValue> _dictionary;

    public ReadOnlyDictionary()
    {
        _dictionary = new Dictionary<TKey, TValue>();
    }

    public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary)
    {
        _dictionary = dictionary;
    }

    #region IDictionary<TKey,TValue> Members

    void IDictionary<TKey, TValue>.Add(TKey key, TValue value)
    {
        throw ReadOnlyException();
    }

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

    public ICollection<TKey> Keys
    {
        get { return _dictionary.Keys; }
    }

    bool IDictionary<TKey, TValue>.Remove(TKey key)
    {
        throw ReadOnlyException();
    }

    public bool TryGetValue(TKey key, out TValue value)
    {
        return _dictionary.TryGetValue(key, out value);
    }

    public ICollection<TValue> Values
    {
        get { return _dictionary.Values; }
    }

    public TValue this[TKey key]
    {
        get
        {
            return _dictionary[key];
        }
    }

    TValue IDictionary<TKey, TValue>.this[TKey key]
    {
        get
        {
            return this[key];
        }
        set
        {
            throw ReadOnlyException();
        }
    }

    #endregion

    #region ICollection<KeyValuePair<TKey,TValue>> Members

    void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
    {
        throw ReadOnlyException();
    }

    void ICollection<KeyValuePair<TKey, TValue>>.Clear()
    {
        throw ReadOnlyException();
    }

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

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

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

    public bool IsReadOnly
    {
        get { return true; }
    }

    bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
    {
        throw ReadOnlyException();
    }

    #endregion

    #region IEnumerable<KeyValuePair<TKey,TValue>> Members

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

    #endregion

    #region IEnumerable Members

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    #endregion

    private static Exception ReadOnlyException()
    {
        return new NotSupportedException("This dictionary is read-only");
    }
}
Thomas Levesque
quelle
11
+1 für das Posten eines vollständigen Codes und nicht nur eines Links, aber ich bin gespannt, wozu ein leerer Konstruktor in einem ReadOnlyDictionary gut ist. :-)
Samuel Neff
20
Achten Sie auf diesen Konstruktor. Wenn Sie eine Referenzkopie des übergebenen Wörterbuchs erstellen, kann ein externer Code Ihr schreibgeschütztes Wörterbuch ändern. Ihr Konstruktor sollte eine vollständige, tiefe Kopie des Arguments erstellen.
Askheaves
25
@askheaves: Gute Beobachtung, aber es ist tatsächlich ziemlich oft nützlich, die Originalreferenz in den schreibgeschützten Typen zu verwenden - behalten Sie das Original in Ihrer privaten Variablen und ändern Sie es für externe Verbraucher. Überprüfen Sie beispielsweise die eingebauten Objekte ReadOnlyObservableCollection oder ReadOnlyCollection: Thomas hat etwas bereitgestellt, das genau so funktioniert, wie es dem .Net-Framework eigen ist. Danke Thomas! +1
Matt DeKrey
13
@ user420667: Die Art und Weise, wie es implementiert ist, ist eine "schreibgeschützte Ansicht eines nicht schreibgeschützten Wörterbuchs". Ein anderer Code kann den Inhalt des ursprünglichen Wörterbuchs ändern, und diese Änderungen werden im schreibgeschützten Wörterbuch wiedergegeben. Es könnte das gewünschte Verhalten sein oder nicht, je nachdem, was Sie erreichen möchten ...
Thomas Levesque
6
@Thomas: Dies entspricht einer ReadOnlyCollection in der .NET BCL. Es ist eine schreibgeschützte Ansicht einer möglicherweise veränderlichen Sammlung. ReadOnly bedeutet nicht unveränderlich und Unveränderlichkeit sollte nicht erwartet werden.
Jeff Yates
229

.NET 4.5

Die .NET Framework 4.5 BCL wird eingeführt ReadOnlyDictionary<TKey, TValue>( Quelle ).

Da die .NET Framework 4.5 BCL keine AsReadOnlyWörterbücher enthält, müssen Sie Ihre eigenen schreiben (wenn Sie dies möchten). Es wäre ungefähr das Folgende, dessen Einfachheit vielleicht deutlich macht, warum es für .NET 4.5 keine Priorität hatte.

public static ReadOnlyDictionary<TKey, TValue> AsReadOnly<TKey, TValue>(
    this IDictionary<TKey, TValue> dictionary)
{
    return new ReadOnlyDictionary<TKey, TValue>(dictionary);
}

.NET 4.0 und niedriger

Vor .NET 4.5 gibt es keine .NET Framework-Klasse, Dictionary<TKey, TValue>die eine Liste wie die ReadOnlyCollection umschließt . Es ist jedoch nicht schwierig, eine zu erstellen.

Hier ist ein Beispiel - es gibt viele andere, wenn Sie Google für ReadOnlyDictionary .

Jeff Yates
quelle
7
Es sieht nicht so aus, als hätten sie daran gedacht, eine AsReadOnly()übliche Methode zu entwickeln Dictionary<,>, also frage ich mich, wie viele Menschen ihren neuen Typ entdecken werden. Dieser Stapelüberlauf-Thread hilft jedoch.
Jeppe Stig Nielsen
@ Jeppe: Ich bezweifle, dass es etwas mit Erinnern zu tun hat. Jedes Feature kostet und ich bezweifle, dass AsReadOnly ganz oben auf der Prioritätenliste stand, zumal es so einfach zu schreiben ist.
Jeff Yates
1
Beachten Sie, dass dies einfach ein Wrapper ist. Änderungen am zugrunde liegenden Wörterbuch (das an den Konstruktor übergeben wurde) mutieren weiterhin das schreibgeschützte Wörterbuch. Siehe auch stackoverflow.com/questions/139592/…
TrueWill
1
@JeffYates Wenn man bedenkt, wie einfach es ist, hätte das Schreiben weniger Zeit in Anspruch genommen als die Entscheidung, ob man Zeit damit verbringen soll, es zu schreiben oder nicht. Aus diesem Grund ist meine Wette auf "sie haben vergessen".
Dan Bechard
Wie TrueWill feststellte, kann das zugrunde liegende Wörterbuch weiterhin mutiert werden. Möglicherweise möchten Sie einen Klon des ursprünglichen Wörterbuchs an den Konstruktor übergeben, wenn Sie eine echte Unveränderlichkeit wünschen (vorausgesetzt, Schlüssel und
Werttyp
19

Auf der jüngsten BUILD-Konferenz wurde bekannt gegeben, dass die Schnittstelle seit .NET 4.5 System.Collections.Generic.IReadOnlyDictionary<TKey,TValue>enthalten ist. Der Beweis ist hier (Mono) und hier (Microsoft);)

Ich bin mir nicht sicher, ob ReadOnlyDictionaryes auch enthalten ist, aber zumindest mit der Schnittstelle sollte es nicht schwierig sein, jetzt eine Implementierung zu erstellen, die eine offizielle generische .NET-Schnittstelle verfügbar macht :)

knocte
quelle
5
ReadOnlyDictionary<TKey, TValue>(.Net 4.5) - msdn.microsoft.com/en-us/library/gg712875.aspx
myermian
18

Fühlen Sie sich frei, meine einfache Hülle zu verwenden. IDictionary wird NICHT implementiert, daher müssen zur Laufzeit keine Ausnahmen für Wörterbuchmethoden ausgelöst werden, die das Wörterbuch ändern würden. Änderungsmethoden sind einfach nicht da. Ich habe dafür meine eigene Schnittstelle namens IReadOnlyDictionary erstellt.

public interface IReadOnlyDictionary<TKey, TValue> : IEnumerable
{
    bool ContainsKey(TKey key);
    ICollection<TKey> Keys { get; }
    ICollection<TValue> Values { get; }
    int Count { get; }
    bool TryGetValue(TKey key, out TValue value);
    TValue this[TKey key] { get; }
    bool Contains(KeyValuePair<TKey, TValue> item);
    void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex);
    IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator();
}

public class ReadOnlyDictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue>
{
    readonly IDictionary<TKey, TValue> _dictionary;
    public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary)
    {
        _dictionary = dictionary;
    }
    public bool ContainsKey(TKey key) { return _dictionary.ContainsKey(key); }
    public ICollection<TKey> Keys { get { return _dictionary.Keys; } }
    public bool TryGetValue(TKey key, out TValue value) { return _dictionary.TryGetValue(key, out value); }
    public ICollection<TValue> Values { get { return _dictionary.Values; } }
    public TValue this[TKey key] { get { return _dictionary[key]; } }
    public bool Contains(KeyValuePair<TKey, TValue> item) { return _dictionary.Contains(item); }
    public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) { _dictionary.CopyTo(array, arrayIndex); }
    public int Count { get { return _dictionary.Count; } }
    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { return _dictionary.GetEnumerator(); }
    IEnumerator IEnumerable.GetEnumerator() { return _dictionary.GetEnumerator(); }
}
Dale Barnard
quelle
4
+1 für die Nichtverletzung des IDictionaryVertrages. Ich denke, es ist aus OOP-Sicht korrekter, von IDictionaryzu erben IReadOnlyDictionary.
Sam
@ Sam Einverstanden, und wenn wir zurückgehen könnten, denke ich, wäre es das Beste und Richtigste, IDictionary(für aktuell IReadOnlyDictionary) und IMutableDictionary(für aktuell IDictionary) zu haben.
MasterMastic
1
@ MasterMastic Das ist ein bizarrer Vorschlag. Ich erinnere mich nicht an andere integrierte Klassen, die auf der umgekehrten Annahme beruhen, dass eine unveränderliche Sammlung das ist, was ein Benutzer standardmäßig erwartet.
Dan Bechard
11

IsReadOnly on IDictionary<TKey,TValue>wird von ICollection<T>( IDictionary<TKey,TValue>erweitert ICollection<T>als ICollection<KeyValuePair<TKey,TValue>>) geerbt . Es wird in keiner Weise verwendet oder implementiert (und wird tatsächlich durch die explizite Implementierung der zugehörigen ICollection<T>Mitglieder "versteckt" ).

Es gibt mindestens drei Möglichkeiten, um das Problem zu lösen:

  1. Implementieren Sie ein benutzerdefiniertes schreibgeschütztes Element IDictionary<TKey, TValue>und verpacken / delegieren Sie es wie vorgeschlagen in ein inneres Wörterbuch
  2. Geben Sie einen ICollection<KeyValuePair<TKey, TValue>>Satz als schreibgeschützt oder IEnumerable<KeyValuePair<TKey, TValue>>abhängig von der Verwendung des Werts zurück
  3. Klonen Sie das Wörterbuch mit dem Kopierkonstruktor .ctor(IDictionary<TKey, TValue>)und geben Sie eine Kopie zurück. Auf diese Weise kann der Benutzer nach Belieben damit umgehen und hat keinen Einfluss auf den Status des Objekts, in dem sich das Quellwörterbuch befindet. Beachten Sie, dass Sie, wenn das zu klonende Wörterbuch Referenztypen enthält (keine Zeichenfolgen wie im Beispiel gezeigt), das Kopieren "manuell" durchführen und auch die Referenztypen klonen müssen.

Nebenbei; Versuchen Sie beim Offenlegen von Sammlungen, die kleinstmögliche Schnittstelle verfügbar zu machen. Im Beispielfall sollte es sich um IDictionary handeln, da Sie so die zugrunde liegende Implementierung variieren können, ohne den öffentlichen Vertrag zu brechen, den der Typ verfügbar macht.

Neal
quelle
8

Ein schreibgeschütztes Wörterbuch kann bis zu einem gewissen Grad durch Folgendes ersetzt werden Func<TKey, TValue>: Ich verwende dies normalerweise in einer API, wenn ich nur möchte, dass Personen Suchvorgänge durchführen. Es ist einfach und insbesondere einfach, das Backend zu ersetzen, falls Sie dies jemals wünschen. Die Liste der Schlüssel wird jedoch nicht angezeigt. Ob das wichtig ist, hängt davon ab, was Sie tun.

Eamon Nerbonne
quelle
4

Nein, aber es wäre einfach, deine eigenen zu rollen. IDictionary definiert eine IsReadOnly-Eigenschaft. Wickeln Sie einfach ein Wörterbuch ein und lösen Sie eine NotSupportedException aus den entsprechenden Methoden aus.

wekempf
quelle
3

Keine in der BCL verfügbar. Ich habe jedoch ein ReadOnlyDictionary (mit dem Namen ImmutableMap) in meinem BCL-Extras-Projekt veröffentlicht

Es ist nicht nur ein vollständig unveränderliches Wörterbuch, sondern unterstützt auch die Erstellung eines Proxy-Objekts, das IDictionary implementiert und an jedem Ort verwendet werden kann, an dem IDictionary verwendet wird. Es wird eine Ausnahme ausgelöst, wenn eine der mutierenden APIs aufgerufen wird

void Example() { 
  var map = ImmutableMap.Create<int,string>();
  map = map.Add(42,"foobar");
  IDictionary<int,string> dictionary = CollectionUtility.ToIDictionary(map);
}
JaredPar
quelle
9
Ihre ImmutableMap ist als ausgeglichener Baum implementiert. Da in .NET im Allgemeinen erwartet wird, dass ein "Wörterbuch" über Hashing implementiert wird - und die entsprechenden Komplexitätseigenschaften aufweist - sollten Sie vorsichtig sein, wenn Sie ImmutableMap als "Wörterbuch" bewerben.
Glenn Slayden
Es scheint, dass die Websites code.msdn.com nicht mehr funktionieren. BCLextras jetzt hier github.com/scottwis/tiny/tree/master/third-party/BclExtras
BozoJoe
1

Sie können eine Klasse erstellen, die nur eine teilweise Implementierung des Wörterbuchs implementiert und alle Funktionen zum Hinzufügen / Entfernen / Festlegen verbirgt.

Verwenden Sie intern ein Wörterbuch, an das die externe Klasse alle Anforderungen weiterleitet.

Da Ihr Wörterbuch wahrscheinlich Referenztypen enthält, können Sie den Benutzer auf keinen Fall daran hindern, Werte für die vom Wörterbuch gehaltenen Klassen festzulegen (es sei denn, diese Klassen selbst sind schreibgeschützt).

Jason Coyne
quelle
1

Ich glaube nicht, dass es einen einfachen Weg gibt ... Wenn Ihr Wörterbuch Teil einer benutzerdefinierten Klasse ist, können Sie dies mit einem Indexer erreichen:

public class MyClass
{
  private Dictionary<string, string> _myDictionary;

  public string this[string index]
  {
    get { return _myDictionary[index]; }
  }
}
Jonas
quelle
Ich muss in der Lage sein, das gesamte Wörterbuch sowie einen Indexer verfügbar zu machen.
Rob Sobers
Dies scheint eine sehr gute Lösung zu sein. Die Clients der Klasse MyClass müssen jedoch möglicherweise mehr über das Wörterbuch wissen, um es beispielsweise durchlaufen zu können. Und was ist, wenn kein Schlüssel vorhanden ist (möglicherweise ist es eine gute Idee, TryGetValue () in irgendeiner Form verfügbar zu machen)? Können Sie Ihre Antwort und Ihren Beispielcode vollständiger machen?
Peter Mortensen
1

+1 Großartige Arbeit, Thomas. Ich bin mit ReadOnlyDictionary noch einen Schritt weiter gegangen.

Ähnlich wie Dale-Lösung, wollte ich entfernen Add(), Clear(), Remove(), etc von IntelliSense. Aber ich wollte, dass meine abgeleiteten Objekte implementiert werden IDictionary<TKey, TValue>.

Außerdem möchte ich, dass der folgende Code kaputt geht: (Auch dies macht Dales Lösung)

ReadOnlyDictionary<int, int> test = new ReadOnlyDictionary<int,int>(new Dictionary<int, int> { { 1, 1} });
test.Add(2, 1);  //CS1061

Die Zeile Add () führt zu:

error CS1061: 'System.Collections.Generic.ReadOnlyDictionary<int,int>' does not contain a definition for 'Add' and no extension method 'Add' accepting a first argument 

Der Anrufer kann es weiterhin übertragen IDictionary<TKey, TValue>, aber das NotSupportedExceptionwird ausgelöst, wenn Sie versuchen, die nicht schreibgeschützten Mitglieder zu verwenden (aus Thomas 'Lösung).

Wie auch immer, hier ist meine Lösung für alle, die dies auch wollten:

namespace System.Collections.Generic
{
    public class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
    {
        const string READ_ONLY_ERROR_MESSAGE = "This dictionary is read-only";

        protected IDictionary<TKey, TValue> _Dictionary;

        public ReadOnlyDictionary()
        {
            _Dictionary = new Dictionary<TKey, TValue>();
        }

        public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary)
        {
            _Dictionary = dictionary;
        }

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

        public ICollection<TKey> Keys
        {
            get { return _Dictionary.Keys; }
        }

        public bool TryGetValue(TKey key, out TValue value)
        {
            return _Dictionary.TryGetValue(key, out value);
        }

        public ICollection<TValue> Values
        {
            get { return _Dictionary.Values; }
        }

        public TValue this[TKey key]
        {
            get { return _Dictionary[key]; }
            set { throw new NotSupportedException(READ_ONLY_ERROR_MESSAGE); }
        }

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

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

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

        public bool IsReadOnly
        {
            get { return true; }
        }

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

        IEnumerator IEnumerable.GetEnumerator()
        {
            return (_Dictionary as IEnumerable).GetEnumerator();
        }

        void IDictionary<TKey, TValue>.Add(TKey key, TValue value)
        {
            throw new NotSupportedException(READ_ONLY_ERROR_MESSAGE);
        }

        bool IDictionary<TKey, TValue>.Remove(TKey key)
        {
            throw new NotSupportedException(READ_ONLY_ERROR_MESSAGE);
        }

        void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
        {
            throw new NotSupportedException(READ_ONLY_ERROR_MESSAGE);
        }

        void ICollection<KeyValuePair<TKey, TValue>>.Clear()
        {
            throw new NotSupportedException(READ_ONLY_ERROR_MESSAGE);
        }

        bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
        {
            throw new NotSupportedException(READ_ONLY_ERROR_MESSAGE);
        }
    }
}
Robert H.
quelle
0
public IEnumerable<KeyValuePair<string, string>> MyDictionary()
{
    foreach(KeyValuePair<string, string> item in _mydictionary)
        yield return item;
}
shahkalpesh
quelle
2
Oder Sie können tun:public IEnumerable<KeyValuePair<string, string>> MyDictionary() { return _mydictionary; }
Pat
0

Dies ist eine schlechte Lösung, siehe unten.

Für diejenigen, die noch .NET 4.0 oder früher verwenden, habe ich eine Klasse, die genau wie die in der akzeptierten Antwort funktioniert, aber viel kürzer ist. Es erweitert das vorhandene Dictionary-Objekt und überschreibt (tatsächlich versteckt) bestimmte Mitglieder, damit sie beim Aufruf eine Ausnahme auslösen.

Wenn der Aufrufer versucht, Add, Remove oder eine andere Mutationsoperation des integrierten Wörterbuchs aufzurufen, gibt der Compiler einen Fehler aus. Ich verwende die veralteten Attribute, um diese Compilerfehler auszulösen. Auf diese Weise können Sie ein Wörterbuch durch dieses ReadOnlyDictionary ersetzen und sofort erkennen, wo Probleme auftreten können, ohne Ihre Anwendung ausführen und auf Laufzeitausnahmen warten zu müssen.

Schau mal:

public class ReadOnlyException : Exception
{
}

public class ReadOnlyDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
    public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary)
        : base(dictionary) { }

    public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)
        : base(dictionary, comparer) { }

    //The following four constructors don't make sense for a read-only dictionary

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public ReadOnlyDictionary() { throw new ReadOnlyException(); }

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public ReadOnlyDictionary(IEqualityComparer<TKey> comparer) { throw new ReadOnlyException(); }

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public ReadOnlyDictionary(int capacity) { throw new ReadOnlyException(); }

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public ReadOnlyDictionary(int capacity, IEqualityComparer<TKey> comparer) { throw new ReadOnlyException(); }


    //Use hiding to override the behavior of the following four members
    public new TValue this[TKey key]
    {
        get { return base[key]; }
        //The lack of a set accessor hides the Dictionary.this[] setter
    }

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public new void Add(TKey key, TValue value) { throw new ReadOnlyException(); }

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public new void Clear() { throw new ReadOnlyException(); }

    [Obsolete("Not Supported for ReadOnlyDictionaries", true)]
    public new bool Remove(TKey key) { throw new ReadOnlyException(); }
}

Diese Lösung weist ein Problem auf, auf das @supercat hier hinweist:

var dict = new Dictionary<int, string>
{
    { 1, "one" },
    { 2, "two" },
    { 3, "three" },
};

var rodict = new ReadOnlyDictionary<int, string>(dict);
var rwdict = rodict as Dictionary<int, string>;
rwdict.Add(4, "four");

foreach (var item in rodict)
{
    Console.WriteLine("{0}, {1}", item.Key, item.Value);
}

Anstatt wie erwartet einen Fehler bei der Kompilierung oder eine von mir erhoffte Laufzeitausnahme anzugeben, wird dieser Code fehlerfrei ausgeführt. Es werden vier Zahlen gedruckt. Das macht mein ReadOnlyDictionary zu einem ReadWriteDictionary.

user2023861
quelle
Das Problem bei diesem Ansatz besteht darin, dass ein solches Objekt an eine Methode übergeben werden kann, die eine Dictionary<TKey,TValue>ohne Compiler-Beschwerde erwartet , und das Umwandeln oder Erzwingen eines Verweises auf den Typ des einfachen Wörterbuchs alle Schutzmaßnahmen aufhebt.
Superkatze
@ Supercat, Mist, du hast recht. Ich dachte, ich hätte auch eine gute Lösung.
user2023861
Ich erinnere mich, dass ich Dictionarymit einer CloneMethode, die an eine Kette gekettet war , eine Ableitung daraus gemacht habe MemberwiseClone. Leider sollte, während es effizient möglich sein Klon ein Wörterbuch , indem Sie die Hintergrundspeicher Klonen, die Tatsache , dass die Hintergrundspeicher sind privateeher als protectedMittel dort für eine abgeleitete Klasse Klon sie keinen Weg; MemberwiseCloneWenn Sie die Hintergrundspeicher verwenden, ohne sie auch zu klonen, wird der Klon durch nachfolgende Änderungen am ursprünglichen Wörterbuch beschädigt, und durch Änderungen am Klon wird das Original beschädigt.
Supercat