Domanda

C'è qualcosa di integrato nel core C# librerie che mi può dare una immutabile Dizionario?

Qualcosa lungo le linee di Java:

Collections.unmodifiableMap(myMap);

E giusto per chiarire, non sto cercando di smettere di chiavi / valori hanno cambiato, solo la struttura del Dizionario.Voglio qualcosa che non riesce veloce e forte se uno qualsiasi di IDictionary del mutatore metodi sono chiamati (Add, Remove, Clear).

È stato utile?

Soluzione

No, ma un wrapper è piuttosto banale:

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

Ovviamente, è possibile modificare il presente[] setter sopra, se si desidera consentire la modifica dei valori.

Altri suggerimenti

Con il rilascio di .NET 4.5, c'è un nuovo ReadOnlyDictionary classe.È semplicemente passare un IDictionary al costruttore per creare immutabile dizionario.

Qui è un utile estensione per il metodo che può essere utilizzato per semplificare la creazione di il readonly dizionario.

Io non la penso così.C'è un modo per creare una Lista di sola lettura e leggere solo la Raccolta, ma non credo che c'è un costruito in sola lettura Dizionario.Sistema.ServiceModel ha un ReadOnlyDictinoary attuazione, ma il suo interno.Probabilmente non sarebbe troppo difficile da copiare, anche se, usando il Riflettore, o semplicemente creare il proprio da zero.Fondamentalmente, si avvolge un Dizionario e getta quando un mutator è chiamato.

Aggiunta su dbkk risposta, Volevo essere in grado di utilizzare un inizializzatore di oggetto quando prima di creare il mio ReadOnlyDictionary.Ho fatto le seguenti modifiche:

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

Ora posso usare la classe in questo modo:

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

L'open-source PowerCollections biblioteca include un solo dizionario wrapper (così come di sola lettura wrapper per praticamente tutto il resto), accessibili per via statica ReadOnly() metodo Algorithms classe.

Una soluzione potrebbe essere, lanciare un nuovo elenco di KeyValuePair dal Dizionario di mantenere l'originale non modificato.

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;

In questo modo il dizionario originale non vengono modificati.

"Out of the box" non c'è un modo per fare questo.È possibile creare una derivazione di un Dizionario di classe e di attuare le restrizioni di cui hai bisogno.

Ho trovato un'implementazione di un Inmutable (non READONLY) realizzazione di un AVLTree per C#.

Un albero AVL ha logaritmica (non costante) costo per ogni operazione, ma stills veloce.

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

Dal Linq, c'è una interfaccia generica ILookup.Leggi di più MSDN.

Pertanto, Per ottenere semplicemente immutabile dizionario si può chiamare:

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

Si può provare qualcosa di simile a questo:

private readonly Dictionary<string, string> _someDictionary;

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

Questo permetterebbe di eliminare la mutevolezza problema a favore di avere il chiamante deve convertire al proprio dizionario:

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

...o utilizzare un'operazione di confronto sul tasto piuttosto che un indice di ricerca, ad es.:

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

In generale, è un'idea molto migliore per non passare intorno a qualsiasi dizionari in primo luogo (se non necessario).

Invece di creare un dominio di oggetti con un'interfaccia che non offre metodi la modifica il dizionario (che gira).Invece offerta di Ricerca richiesto-metodo che recupera elemento da dizionario chiave (bonus rende più facile da usare di un dizionario così).

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

C'è anche un'altra alternativa, come ho descritto in:

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

Essenzialmente è una sottoclasse di ReadOnlyCollection>, che ottiene il lavoro fatto in un modo più elegante.Elegante, nel senso che si ha in fase di compilazione supporto per la realizzazione del Dizionario di sola lettura, piuttosto che la generazione di eccezioni da metodi che modificano gli elementi all'interno di esso.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top