Czy w .NET jest dostępny słownik ogólny tylko do odczytu?

186

Zwracam odwołanie do słownika w mojej właściwości tylko do odczytu. Jak uniemożliwić konsumentom zmianę moich danych? Gdyby to był IList, mógłbym go po prostu zwrócić AsReadOnly. Czy jest coś podobnego, co mogę zrobić ze słownikiem?

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
źródło
4
Musi istnieć jakiś sposób na zrobienie tego, inaczej nie byłoby właściwości IsReadOnly w IDictionary ... ( msdn.microsoft.com/en-us/library/bb338949.aspx )
Powerlord
2
Wiele koncepcyjnych korzyści wynikających z niezmienności można uzyskać bez wymuszania jej przez środowisko wykonawcze. Jeśli jest to projekt prywatny, rozważ zdyscyplinowaną, nieformalną metodę. Jeśli musisz podać dane konsumentowi, powinieneś poważnie rozważyć głębokie kopie. Jeśli weźmiesz pod uwagę, że niezmienna kolekcja wymaga 1) niezmiennego odwołania do kolekcji 2) zapobiegania mutowaniu samej sekwencji i 3) zapobiegania modyfikowaniu właściwości elementów kolekcji oraz że niektóre z nich mogą zostać naruszone przez odbicie, wymuszanie w czasie wykonywania jest Niepraktyczne.
Sprague
27
Od .NET 4.5 istnieje System.Collections.ObjectModel.ReadOnlyDictionary ^ _ ^
Smartkid
2
Istnieją również niezmienne kolekcje firmy Microsoft za pośrednictwem NuGet msdn.microsoft.com/en-us/library/dn385366%28v=vs.110%29.aspx
VoteCoffee

Odpowiedzi:

156

Oto prosta implementacja, która otacza słownik:

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
źródło
11
+1 za wysłanie pełnego kodu, a nie tylko linku, ale jestem ciekawy, jaki jest sens pustego konstruktora w ReadOnlyDictionary? :-)
Samuel Neff
20
Uważaj na tego konstruktora. Jeśli wykonasz kopię referencyjną przekazanego słownika, istnieje możliwość, że zewnętrzny fragment kodu zmodyfikuje Twój słownik „tylko do odczytu”. Twój konstruktor powinien wykonać pełną, głęboką kopię argumentu.
askheaves
25
@askheaves: Dobra obserwacja, ale w rzeczywistości dość często przydatne jest użycie oryginalnego odniesienia w typach tylko do odczytu - zachowaj w swojej prywatnej zmiennej oryginał i zmodyfikuj go dla zewnętrznych konsumentów. Na przykład sprawdź ReadOnlyObservableCollection lub obiekty ReadOnlyCollection, które są wbudowane: Thomas dostarczył coś, co działa dokładnie tak samo, jak te, które są nieodłączne od frameworka .Net. Dzięki Thomas! +1
Matt DeKrey,
13
@ user420667: sposób, w jaki jest zaimplementowany, jest „widokiem słownika tylko do odczytu”. Inny kod może zmienić zawartość oryginalnego słownika, a te zmiany zostaną odzwierciedlone w słowniku tylko do odczytu. Może to być pożądane zachowanie lub nie, w zależności od tego, co chcesz osiągnąć ...
Thomas Levesque,
6
@Thomas: To jest to samo, co ReadOnlyCollection w .NET BCL. Jest to widok tylko do odczytu kolekcji, która może podlegać zmianom. ReadOnly nie oznacza niezmienności i niezmienności nie należy oczekiwać.
Jeff Yates
229

.NET 4.5

NET Framework 4.5 BCL wprowadza ReadOnlyDictionary<TKey, TValue>( źródło ).

Ponieważ .NET Framework 4.5 BCL nie zawiera AsReadOnlysłowników for, będziesz musiał napisać własny (jeśli chcesz). Byłoby to coś podobnego do poniższego, którego prostota może podkreślać, dlaczego nie był to priorytet dla .NET 4.5.

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

.NET 4.0 i starsze

W wersjach starszych niż .NET 4.5 nie ma klasy platformy .NET, która opakowuje element Dictionary<TKey, TValue>podobny do ReadOnlyCollection, który zawija listę . Jednak nie jest trudno go stworzyć.

Oto przykład - jest wiele innych, jeśli wyszukujesz ReadOnlyDictionary .

Jeff Yates
źródło
7
Wygląda na to, że nie pamiętali o zrobieniu AsReadOnly()metody na zwykłej metodzie Dictionary<,>, więc zastanawiam się, ile osób odkryje swój nowy typ. Jednak ten wątek przepełnienia stosu pomoże.
Jeppe Stig Nielsen
@Jeppe: Wątpię, czy ma to coś wspólnego z pamięcią. Każda funkcja kosztuje i wątpię, czy AsReadOnly był wysoko na liście priorytetów, zwłaszcza, że ​​jest tak łatwy do napisania.
Jeff Yates
1
Zauważ, że jest to po prostu opakowanie; zmiany w podstawowym słowniku (tym przekazanym do konstruktora) nadal będą powodować mutację słownika tylko do odczytu. Zobacz także stackoverflow.com/questions/139592/…
TrueWill
1
@JeffYates Biorąc pod uwagę, jak proste jest to, napisanie tego zajęłoby mniej czasu niż podjęcie decyzji, czy poświęcić czas na pisanie. Z tego powodu stawiam na „zapomnieli”.
Dan Bechard
Jak stwierdził TrueWill, słownik bazowy nadal można zmutować. Możesz rozważyć przekazanie klonu oryginalnego słownika do konstruktora, jeśli chcesz prawdziwej niezmienności (zakładając, że klucz i typ wartości są również niezmienne).
user420667
19

Podczas niedawnej konferencji BUILD ogłoszono, że od .NET 4.5 interfejs System.Collections.Generic.IReadOnlyDictionary<TKey,TValue>jest dołączony. Dowód jest tutaj (Mono) i tutaj (Microsoft);)

Nie jestem pewien, czy ReadOnlyDictionaryjest dołączony, ale przynajmniej z interfejsem nie powinno być trudne stworzenie teraz implementacji, która ujawnia oficjalny ogólny interfejs .NET :)

knocte
źródło
5
ReadOnlyDictionary<TKey, TValue>(.Net 4.5) - msdn.microsoft.com/en-us/library/gg712875.aspx
myermian
18

Zapraszam do korzystania z mojego prostego opakowania. NIE implementuje IDictionary, więc nie musi generować wyjątków w czasie wykonywania dla metod słownikowych, które zmieniłyby słownik. Metody zmiany po prostu nie istnieją. Zrobiłem dla niego własny interfejs o nazwie IReadOnlyDictionary.

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
źródło
4
+1 za nienaruszanie IDictionaryumowy. Myślę, że z punktu widzenia OOP lepiej jest IDictionarydziedziczyć z IReadOnlyDictionary.
Sam
@Sam Zgoda, i jeśli moglibyśmy wrócić, myślę, że najlepiej i najbardziej poprawne byłoby posiadanie IDictionary(dla prądu IReadOnlyDictionary) i IMutableDictionary(dla prądu IDictionary).
MasterMastic
1
@MasterMastic To dziwna propozycja. Nie przypominam sobie żadnych innych wbudowanych klas opierających się na odwrotnym założeniu, że niezmienna kolekcja jest tym, czego domyślnie oczekuje użytkownik.
Dan Bechard
11

IsReadOnly on IDictionary<TKey,TValue>jest dziedziczony z ICollection<T>( IDictionary<TKey,TValue>rozszerza się ICollection<T>jako ICollection<KeyValuePair<TKey,TValue>>). Nie jest w żaden sposób używany ani implementowany (w rzeczywistości jest „ukryty” poprzez użycie jawnej implementacji skojarzonegoICollection<T> członków).

Istnieją co najmniej 3 sposoby rozwiązania problemu:

  1. Zaimplementuj niestandardowy plik tylko do odczytu IDictionary<TKey, TValue> i zawiń / deleguj do słownika wewnętrznego, zgodnie z sugestią
  2. Zwróć ICollection<KeyValuePair<TKey, TValue>>zestaw jako tylko do odczytu lub plik IEnumerable<KeyValuePair<TKey, TValue>> zależności od użycia wartości
  3. Sklonuj słownik za pomocą konstruktora kopiującego .ctor(IDictionary<TKey, TValue>)i zwróć kopię - w ten sposób użytkownik może robić z nim według własnego uznania i nie ma to wpływu na stan obiektu hostującego słownik źródłowy. Zwróć uwagę, że jeśli słownik, który klonujesz, zawiera typy odwołań (nie ciągi znaków, jak pokazano w przykładzie), będziesz musiał wykonać kopiowanie „ręcznie”, a także sklonować typy odwołań.

Tak na marginesie; ujawniając kolekcje, staraj się ujawnić najmniejszy możliwy interfejs - w przykładowym przypadku powinien to być IDictionary, ponieważ umożliwia to różnicowanie podstawowej implementacji bez przerywania kontraktu publicznego, który ujawnia typ.

Neal
źródło
8

Słownik tylko do odczytu można w pewnym stopniu zastąpić Func<TKey, TValue>- zwykle używam go w API, jeśli chcę, aby ludzie przeprowadzali wyszukiwania; jest to proste, aw szczególności można łatwo wymienić backend, jeśli kiedykolwiek zechcesz. Nie zawiera jednak listy kluczy; czy to ma znaczenie, zależy od tego, co robisz.

Eamon Nerbonne
źródło
4

Nie, ale łatwo byłoby zrobić własne. IDictionary definiuje właściwość IsReadOnly. Po prostu zawiń Dictionary i wyrzuć NotSupportedException z odpowiednich metod.

wekempf
źródło
3

Brak dostępnych w BCL. Jednak opublikowałem ReadOnlyDictionary (o nazwie ImmutableMap) w moim projekcie BCL Extras

Oprócz tego, że jest w pełni niezmiennym słownikiem, obsługuje tworzenie obiektu proxy, który implementuje IDictionary i może być używany w dowolnym miejscu, w którym jest używany IDictionary. Wyrzuci wyjątek za każdym razem, gdy zostanie wywołany jeden z mutujących interfejsów API

void Example() { 
  var map = ImmutableMap.Create<int,string>();
  map = map.Add(42,"foobar");
  IDictionary<int,string> dictionary = CollectionUtility.ToIDictionary(map);
}
JaredPar
źródło
9
Twoja ImmutableMap jest zaimplementowana jako zrównoważone drzewo. Ponieważ w .NET ludzie generalnie oczekują, że „słownik” zostanie zaimplementowany za pomocą haszowania - i będzie wykazywał odpowiednie właściwości złożoności - możesz być ostrożny przy promowaniu ImmutableMap jako „słownika”.
Glenn Slayden
Wygląda na to, że witryny code.msdn.com są niedziałające. BCLextras teraz tutaj github.com/scottwis/tiny/tree/master/third-party/BclExtras
BozoJoe
1

Możesz stworzyć klasę, która implementuje tylko częściową implementację słownika i ukrywa wszystkie funkcje add / remove / set.

Użyj słownika wewnętrznie, do którego klasa zewnętrzna przekazuje wszystkie żądania.

Jednakże, ponieważ twój słownik prawdopodobnie zawiera typy referencyjne, nie ma możliwości powstrzymania użytkownika przed ustawieniem wartości klas znajdujących się w słowniku (chyba że same te klasy są tylko do odczytu)

Jason Coyne
źródło
1

Nie sądzę, aby można było to łatwo zrobić ... jeśli Twój słownik jest częścią klasy niestandardowej, możesz to osiągnąć za pomocą indeksatora:

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

  public string this[string index]
  {
    get { return _myDictionary[index]; }
  }
}
Jonas
źródło
Muszę być w stanie udostępnić cały słownik, a także indeksator.
Rob Sobers,
Wydaje się, że to bardzo dobre rozwiązanie. Jednak klienci klasy MyClass mogą potrzebować więcej informacji na temat słownika, na przykład w celu wykonania iteracji. A co, jeśli klucz nie istnieje (może ujawnienie TryGetValue () w jakiejś formie byłoby dobrym pomysłem)? Czy możesz uzupełnić swoją odpowiedź i przykładowy kod?
Peter Mortensen
1

+1 Świetna robota, Thomas. Poszedłem o krok dalej w ReadOnlyDictionary.

Podobnie jak rozwiązanie Dale'a, chciałem usunąć Add(), Clear(), Remove()itp z IntelliSense. Ale chciałem zaimplementować moje obiekty pochodne IDictionary<TKey, TValue>.

Ponadto chciałbym, aby następujący kod się zepsuł: (Ponownie, rozwiązanie Dale'a też to robi)

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

Linia Add () powoduje:

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

Wzywający może nadal przesyłać go do IDictionary<TKey, TValue>, ale NotSupportedExceptionzostanie podniesiony, jeśli spróbujesz użyć elementów członkowskich nie tylko do odczytu (z rozwiązania Thomasa).

W każdym razie, oto moje rozwiązanie dla każdego, kto również tego chciał:

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.
źródło
0
public IEnumerable<KeyValuePair<string, string>> MyDictionary()
{
    foreach(KeyValuePair<string, string> item in _mydictionary)
        yield return item;
}
shahkalpesh
źródło
2
Możesz też:public IEnumerable<KeyValuePair<string, string>> MyDictionary() { return _mydictionary; }
Pat
0

To złe rozwiązanie, patrz na dole.

Dla tych, którzy nadal używają .NET 4.0 lub starszego, mam klasę, która działa tak jak ta w zaakceptowanej odpowiedzi, ale jest znacznie krótsza. Rozszerza istniejący obiekt Dictionary, przesłaniając (faktycznie ukrywając) niektórych członków, aby rzucali wyjątek po wywołaniu.

Jeśli obiekt wywołujący spróbuje wywołać Dodaj, Usuń lub inną operację mutacji, którą ma wbudowany słownik, kompilator zgłosi błąd. Używam atrybutów Przestarzałe, aby podnieść te błędy kompilatora. W ten sposób możesz zastąpić słownik tym ReadOnlyDictionary i natychmiast zobaczyć, gdzie mogą wystąpić problemy, bez konieczności uruchamiania aplikacji i czekania na wyjątki w czasie wykonywania.

Spójrz:

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

W tym rozwiązaniu występuje problem wskazany przez @supercat przedstawiony tutaj:

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

Zamiast podawać błąd w czasie kompilacji, jak się spodziewałem, lub wyjątek czasu wykonania, taki jak miałem nadzieję, ten kod działa bez błędów. Drukuje cztery liczby. To sprawia, że ​​mój ReadOnlyDictionary jest ReadWriteDictionary.

user2023861
źródło
Problem z tym podejściem polega na tym, że taki obiekt może zostać przekazany do metody, która oczekuje Dictionary<TKey,TValue>reklamacji bez żadnego kompilatora, a rzutowanie lub wymuszanie odwołania do typu zwykłego słownika usunie wszelkie zabezpieczenia.
supercat
@supercat, cholera, masz rację. Myślałem, że mam też dobre rozwiązanie.
user2023861
Pamiętam co pochodną Dictionaryze Clonesposobu, który przykuty do MemberwiseClone. Niestety, podczas gdy efektywne klonowanie słownika powinno być możliwe poprzez klonowanie magazynów zapasowych, fakt, że magazyny zapasowe są privateraczej niż protectedoznacza, że ​​nie ma sposobu, aby klasa pochodna mogła je sklonować; używanie MemberwiseClonebez klonowania zapasów oznacza, że ​​kolejne modyfikacje oryginalnego słownika zepsują klon, a modyfikacje wprowadzone w klonie zepsują oryginał.
supercat
0

Dla każdego, kto przychodzi do tego pytania przy użyciu platformy .NET Core, zapoznaj się z System.Collections.Immutable .

W szczególności w przypadku niezmiennych słowników możesz użyć ImmutableDictionary<TKey, TValue>.Builder:

public static ImmutableDictionary<string, string> ToReadOnly(this IDictionary<String, string> d)
{
    var builder = new ImmutableDictionary<string, string>.Builder();
    builder.AddRange(d);
    return builder.ToImmutable();
}
crimbo
źródło