C# 有办法给我一个不可变的字典吗?
-
09-06-2019 - |
题
核心 C# 库中是否内置了任何可以为我提供不可变字典的内容?
类似的东西 爪哇的:
Collections.unmodifiableMap(myMap);
只是为了澄清,我不想阻止键/值本身被更改,只是阻止字典的结构。如果调用任何 IDictionary 的 mutator 方法,我想要一些快速而大声地失败的东西(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[] setter。
其他提示
我不这么认为。有一种方法可以创建只读列表和只读集合,但我不认为有内置的只读字典。System.ServiceModel 有一个 ReadOnlyDictinoary 实现,但它是内部的。不过,使用 Reflector 复制它或者简单地从头开始创建自己的代码可能不会太难。它基本上包装了一个 Dictionary 并在调用 mutator 时抛出异常。
添加到 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
班级。
一种解决方法可能是,从字典中抛出一个新的 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 类并实现所需的限制来创建一个字典类。
我在这里找到了 AVLTree for C# 的不可变(非只读)实现的实现。
AVL 树的每个操作的成本都是对数(不是常数),但仍然很快。
从 Linq 开始,有一个通用接口 查询. 。阅读更多内容 微软软件定义网络.
因此,要简单地获取不可变字典,您可以调用:
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> 的子类,它以更优雅的方式完成工作。优雅是因为它具有编译时支持,使字典只读,而不是从修改其中项目的方法中抛出异常。