C# には不変の辞書を提供する方法はありますか?
-
09-06-2019 - |
質問
不変の辞書を提供できるものはコア C# ライブラリに組み込まれていますか?
の線に沿った何か ジャワの:
Collections.unmodifiableMap(myMap);
明確にしておきますが、私はキーや値自体の変更を止めようとしているのではなく、ディクショナリの構造だけを止めようとしているだけです。IDictionary のミューテーター メソッドのいずれかが呼び出された場合に、高速かつ大音量で失敗するものが必要です (Add, Remove, Clear
).
解決
いいえ、しかしラッパーはかなり簡単です:
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();
}
}
明らかに、値の変更を許可したい場合は、上記の this[] セッターを変更できます。
他のヒント
私の知る限り、それはありません。ただし、次の記事からコードをコピーする (そして多くのことを学ぶ) ことはできるかもしれません。
C# における不変性 パート 1:不変性の種類
C# における不変性パート 2:シンプルな不変スタック
C# における不変性パート 3:共変の不変スタック
C# における不変性パート 4:不変のキュー
C# における不変性パート 6:単純な二分木
C# における不変性パート 7:二分木の詳細
C# における不変性パート 8:二分木についてさらに詳しく
C# における不変性パート 9:アカデミック?さらに、私の AVL ツリー実装
C# における不変性パート 10:両端のキュー
C# の不変性パート 11:動作する両端キュー
.NET 4.5 のリリースにより、新しい機能が追加されました。 ReadOnlyDictionary クラス。単に渡すだけです IDictionary
コンストラクターに追加して、不変の辞書を作成します。
ここ は、読み取り専用辞書の作成を簡素化するために使用できる便利な拡張メソッドです。
私はそうは思わない。読み取り専用のリストと読み取り専用のコレクションを作成する方法はありますが、読み取り専用の辞書は組み込まれていないと思います。System.ServiceModel には ReadOnlyDictinoary 実装がありますが、これは内部実装です。ただし、Reflector を使用してそれをコピーしたり、独自のものを最初から作成したりすることは、おそらくそれほど難しいことではありません。基本的に辞書をラップし、ミューテーターが呼び出されたときにスローします。
追加する 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"}
};
オープンソース パワーコレクション ライブラリには、静的メソッド経由でアクセスできる読み取り専用の辞書ラッパー (その他ほとんどすべての読み取り専用ラッパー) が含まれています。 ReadOnly()
のメソッド Algorithms
クラス。
回避策の 1 つは、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;
こうすることで、元の辞書は変更されなくなります。
「すぐに使える」これを行う方法はありません。独自の Dictionary クラスを派生し、必要な制限を実装することで、辞書を作成できます。
C# 用の AVLTree の不変 (READONLY ではない) 実装の実装をここで見つけました。
AVL ツリーの各操作には対数 (一定ではない) コストがかかりますが、それでも高速です。
Linq 以来、汎用インターフェイスがあります ILookup. 。続きを読む MSDN.
したがって、単に不変の辞書を取得するには、次のように呼び出すことができます。
using System.Linq;
// (...)
var dictionary = new Dictionary<string, object>();
// (...)
var read_only = dictionary.ToLookup(kv => kv.Key, kv => kv.Value);
次のようなことを試すことができます。
private readonly Dictionary<string, string> _someDictionary;
public IEnumerable<KeyValuePair<string, string>> SomeDictionary
{
get { return _someDictionary; }
}
これにより、呼び出し元が独自の辞書に変換する必要がなくなり、可変性の問題が解決されます。
foo.SomeDictionary.ToDictionary(kvp => kvp.Key);
...または、インデックス検索ではなくキーの比較演算を使用します。例:
foo.SomeDictionary.First(kvp => kvp.Key == "SomeKey");
一般に、最初から辞書を渡さない方がはるかに良い考えです (そうする必要がない場合)。
代わりに、メソッドを提供しないインターフェイスを持つドメイン オブジェクトを作成します。 修正する 辞書(それがラップするもの)。代わりに、キーによって辞書から要素を取得する必要な 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...};
}
私が説明したように、別の代替手段もあります。
http://www.softwarerockstar.com/2010/10/readonlydictionary-tkey-tvalue/
基本的に、これは ReadOnlyCollection> のサブクラスであり、よりエレガントな方法で作業を実行します。Dictionary 内の項目を変更するメソッドから例外をスローするのではなく、Dictionary を読み取り専用にするコンパイル時のサポートがあるという意味でエレガントです。