C#は私に不変の辞書を与える方法がありますか?




c# ref keyword (9)

.NET 4.5のリリースには、新しいReadOnlyDictionaryクラスがあります。 不変式辞書を作成するには、コンストラクタにIDictionaryを渡します。

Hereは、読みやすいディクショナリの作成を簡略化するのに役立つ便利な拡張メソッドがあります。

https://code.i-harness.com

私に不変のディクショナリを与えることができるコアのC#ライブラリに何かが組み込まれていますか?

Javaの行に沿った何か:

Collections.unmodifiableMap(myMap);

そして明確にするために、私はキー/値自体が変更されるのをやめさせるのではなく、辞書の構造だけを止めるつもりではありません。 IDictionaryのmutatorメソッドのどれかが呼び出されると( Add, Remove, ClearAdd, Remove, Clear素早く失敗するものが欲しいです。


Linq以来、汎用インタフェースILookupがあります。 MSDN詳しく読む。

したがって、単に不変の辞書を取得するには、

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

dbkkの答えに加えて 、私はReadOnlyDictionaryを最初に作成するときにオブジェクトイニシャライザを使用できるようにしたかったのです。 私は以下の変更を加えました:

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

今私はそのようにクラスを使用することができます:

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

いいえ。しかし、ラッパーはかなり簡単です。

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

明らかに、値の変更を許可する場合は、上記の[]セッターを変更することができます。


一般的には、最初に辞書を回してはいけない方がいいでしょう(あなたがいなければ)。

その代わりに、辞書を変更するメソッド(ラップするもの)を提供しないインターフェースを持つドメインオブジェクトを作成します。 その代わりに、キーで辞書から要素を取り出すための必要なLookUpメソッドを提供します(これは辞書よりも使いやすくします)。

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

回避策の1つは、DictionaryからKeyValuePairの新しいリストを投げて、元のままにしておくことです。

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;

この方法で元の辞書は変更されません。


私が知る限り、そこにはありません。 しかし、あなたはこれらの記事からいくつかのコードをコピーすることができます(そしてたくさん学ぶことができます):

C#の不変性パート1:不変性の種類
C#の不変性第2部:単純な変更不可能なスタック
C#の不変性第3回:共変不可能なスタック
C#での不変性パート4:変更不能なキュー
C#の不変性:第6部:単純なバイナリツリー
C#パート7の不変性:バイナリツリーの詳細
C#パート8の不変性:バイナリツリーのさらに多く
C#の不変性パート9:アカデミック? 私のAVLツリーの実装
C#の不変性パート10:ダブルエンドキュー
C#Part 11における不変性:作業中の両端キュー


私が説明したようにもう一つの選択肢があります:

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

本質的には、ReadOnlyCollection>のサブクラスであり、より洗練された方法で作業が行われます。 それは、その中の項目を変更するメソッドから例外をスローするのではなく、辞書を読み込み専用にするコンパイル時のサポートがあるという意味で優雅です。


私はそうは思わない。 読み取り専用のリストと読み取り専用のコレクションを作成する方法がありますが、読み込み専用の辞書はないと思います。 System.ServiceModelにはReadOnlyDictinoary実装がありますが、内部実装はありません。 Reflectorを使用してコピーしたり、最初から自分自身を作成するのは難しいでしょう。 基本的にDictionaryをラップして、ミューテータが呼び出されるとスローします。





dictionary