Czy C# może dać mi niezmienny Słownik?

Czy jest coś wbudowanego w podstawowe biblioteki C#, co może dać mi niezmienny Słownik?

Coś na wzór Javy :

Collections.unmodifiableMap(myMap);

I tak dla jasności, nie chcę zatrzymywać samych kluczy / wartości przed zmianą, tylko strukturę słownika. Chcę czegoś, co zawiedzie szybko i głośno, jeśli któraś z metod Mutatora IDictionary zostanie wywołana (Add, Remove, Clear).

Author: serg10, 2008-08-29

13 answers

Nie, ale wrapper jest raczej banalny:

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

Oczywiście, możesz zmienić powyższy seter [], jeśli chcesz zezwolić na modyfikację wartości.

 49
Author: dbkk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-08 22:00:52

Wraz z wydaniem. NET 4.5, pojawiła się nowa klasa ReadOnlyDictionary . Po prostu przekazujesz IDictionary do konstruktora, aby utworzyć słownik niezmienny.

Tutaj {[3] } jest pomocną metodą rozszerzenia, która może być użyta do uproszczenia tworzenia słownika readonly.

 14
Author: Dylan Meador,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:31:59

Nie wydaje mi się. Istnieje sposób na stworzenie listy tylko do odczytu i kolekcji tylko do odczytu, ale nie sądzę, że istnieje wbudowany słownik tylko do odczytu. System.ServiceModel ma implementację tylko wczytaną, ale wewnętrzną. Prawdopodobnie nie byłoby zbyt trudne skopiowanie go za pomocą reflektora lub po prostu stworzenie własnego od zera. Zasadniczo zawija Słownik i rzuca, gdy wywoływany jest mutator.

 3
Author: Kevin Dente,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-29 19:02:09

Dodając do odpowiedzi dbkk , chciałem móc użyć inicjalizatora obiektu podczas pierwszego tworzenia ReadOnlyDictionary. Dokonałem następujących modyfikacji:

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

Teraz mogę używać klasy Tak:

ReadOnlyDictionary<string, string> Fields =
    new ReadOnlyDictionary<string, string>(2)
        {
            {"hey", "now"},
            {"you", "there"}
        };
 3
Author: Sarah Vessels,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:31:59

Biblioteka open-source PowerCollections zawiera wrapper słownika tylko do odczytu (jak również wrappery tylko do odczytu dla prawie wszystkiego innego), dostępny za pomocą statycznej metody ReadOnly() w klasie Algorithms.

 3
Author: David Moles,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-24 16:48:47

Jednym z obejść może być rzucenie nowej listy KeyValuePair ze słownika, aby zachować oryginał niezmodyfikowany.

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;

W ten sposób oryginalny słownik nie zostanie zmodyfikowany.

 2
Author: chakrit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-29 19:44:59

"po wyjęciu z pudełka" nie ma na to sposobu. Możesz ją utworzyć, wyprowadzając własną klasę słownika i wdrażając potrzebne ograniczenia.

 1
Author: Scott Dorman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-29 19:24:53

Znalazłem implementację Niewyważalnej (nie tylko READONLY) implementacji AVLTree dla C# tutaj.

Drzewo AVL ma logarytmiczny (nie stały) koszt każdej operacji, ale nadal jest szybki.

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

 1
Author: Olmo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-02 14:21:00

Od Linq istnieje ogólny interfejs ILookup . Czytaj więcej w MSDN .

Dlatego, aby po prostu uzyskać słownik niezmienny, możesz wywołać:

using System.Linq;
// (...)
var dictionary = new Dictionary<string, object>();
// (...)
var read_only = dictionary.ToLookup(kv => kv.Key, kv => kv.Value);
 1
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-15 17:49:50

Możesz spróbować czegoś takiego:

private readonly Dictionary<string, string> _someDictionary;

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

Usunęłoby to problem zmienności na korzyść konieczności przekonwertowania go do własnego słownika:

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

... lub użyć operacji porównania na kluczu, a nie Wyszukiwania indeksu, np.:

foo.SomeDictionary.First(kvp => kvp.Key == "SomeKey");
 0
Author: uglybugger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-21 04:49:16

Ogólnie rzecz biorąc, o wiele lepszym pomysłem jest, aby nie przekazywać żadnych słowników w pierwszej kolejności(jeśli nie musisz).

Zamiast tego-utwórz obiekt domeny z interfejsem, który nie oferuje żadnych metod modyfikujących słownik (który zawija). Zamiast tego oferuje wymagane LookUp-metodę, która pobiera element ze słownika po kluczu (bonus jest to sprawia, że łatwiejsze w użyciu niż słownik, jak również).

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...};
}
 0
Author: Fredrik Solhaug,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-15 17:45:35

Jest też inna alternatywa, którą opisałem na:

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

Zasadniczo jest to podklasa ReadOnlyCollection>, która wykonuje pracę w bardziej elegancki sposób. Elegancki w tym sensie, że ma wsparcie w czasie kompilacji do tworzenia słownika tylko do odczytu, a nie rzucanie wyjątków od metod, które modyfikują elementy w nim zawarte.

 -1
Author: SoftwareRockstar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-30 05:29:50