C # 有办法给我一个不可变的字典吗?

在核心 C # 库中是否有内置的东西可以给我一个不可变的 Dictionary?

类似于 爪哇咖啡的东西:

Collections.unmodifiableMap(myMap);

澄清一下,我并不是要阻止键/值本身被更改,而是要阻止 Dictionary 的结构被更改。如果调用了 IDictionary 的 mutator 方法(Add, Remove, Clear) ,那么我想要一个快速而响亮地失败的方法。

25119 次浏览

我不这么认为。有一种方法可以创建一个只读 List 和只读 Collection,但我不认为有一个内置的只读字典。系统。ServiceModel 有一个 ReadOnlyDictionary 实现,但它的内部。可能不会太难复制,但使用反射器,或只是创建自己从头开始。它基本上包装了一个 Dictionary,当调用 mutator 时抛出。

“开箱即用”没有办法做到这一点。您可以通过派生自己的 Dictionary 类并实现所需的限制来创建一个。

一种解决方案可能是,从 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;

这样原来的字典就不会被修改了。

不,但是包装是相当微不足道的:

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。

我在这里找到了一个 C # AVLTree 的 Inmutable (不是 READONLY)实现。

AVL 树对每个操作都有对数(非常数)开销,但仍然很快。

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

添加到 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"}
};

自 Linq 以来,有一个通用接口 查找。 阅读更多 MSDN

因此,要简单地获得不可变的字典,您可以调用:

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

还有另一种选择,就像我在书中描述的那样:

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

本质上,它是 ReadOnlyCollection > 的一个子类,它以更优雅的方式完成工作。优雅的意义在于,它支持在编译时使 Dictionary 成为只读的,而不是从修改其中项的方法抛出异常。

开源的 电力收集库包含一个只读字典包装器(以及几乎所有其他内容的只读包装器) ,可以通过 Algorithms类上的静态 ReadOnly()方法访问。

你可以试试这样:

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

随着。NET 4.5,有一个新的 只读字典类。您只需向构造函数传递一个 IDictionary来创建不可变的字典。

这里 是一个有用的扩展方法,可以用来简化只读字典的创建。

一般来说,最好不要把任何字典放在首位传阅(如果你没有必要的话)。

相反,创建一个域对象,其接口不提供任何方法 修改字典(它封装)。相反,它提供了所需的 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...};
}

我知道这是一个非常古老的问题,但我在2020年不知怎么地发现了它,所以我认为现在可能值得注意的是,有一种方法可以创建不变的字典:

Https://learn.microsoft.com/en-us/dotnet/api/system.collections.immutable.immutabledictionary.toimmutabledictionary?view=netcore-3.1

用法:

using System.Collections.Immutable;


public MyClass {
private Dictionary<KeyType, ValueType> myDictionary;


public ImmutableDictionary<KeyType, ValueType> GetImmutable()
{
return myDictionary.ToImmutableDictionary();
}
}