.NET 中是否有只读通用字典?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/678379/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is there a read-only generic dictionary available in .NET?
提问by Rob Sobers
I'm returning a reference to a dictionary in my read only property. How do I prevent consumers from changing my data? If this were an IListI could simply return it AsReadOnly. Is there something similar I can do with a dictionary?
我在我的只读属性中返回对字典的引用。如何防止消费者更改我的数据?如果这是一个IList我可以简单地返回它AsReadOnly。我可以用字典做类似的事情吗?
Private _mydictionary As Dictionary(Of String, String)
Public ReadOnly Property MyDictionary() As Dictionary(Of String, String)
Get
Return _mydictionary
End Get
End Property
采纳答案by Thomas Levesque
Here's a simple implementation that wraps a dictionary:
这是一个包装字典的简单实现:
public class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private readonly IDictionary<TKey, TValue> _dictionary;
public ReadOnlyDictionary()
{
_dictionary = new Dictionary<TKey, TValue>();
}
public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary)
{
_dictionary = dictionary;
}
#region IDictionary<TKey,TValue> Members
void IDictionary<TKey, TValue>.Add(TKey key, TValue value)
{
throw ReadOnlyException();
}
public bool ContainsKey(TKey key)
{
return _dictionary.ContainsKey(key);
}
public ICollection<TKey> Keys
{
get { return _dictionary.Keys; }
}
bool IDictionary<TKey, TValue>.Remove(TKey key)
{
throw ReadOnlyException();
}
public bool TryGetValue(TKey key, out TValue value)
{
return _dictionary.TryGetValue(key, out value);
}
public ICollection<TValue> Values
{
get { return _dictionary.Values; }
}
public TValue this[TKey key]
{
get
{
return _dictionary[key];
}
}
TValue IDictionary<TKey, TValue>.this[TKey key]
{
get
{
return this[key];
}
set
{
throw ReadOnlyException();
}
}
#endregion
#region ICollection<KeyValuePair<TKey,TValue>> Members
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
{
throw ReadOnlyException();
}
void ICollection<KeyValuePair<TKey, TValue>>.Clear()
{
throw ReadOnlyException();
}
public bool Contains(KeyValuePair<TKey, TValue> item)
{
return _dictionary.Contains(item);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
_dictionary.CopyTo(array, arrayIndex);
}
public int Count
{
get { return _dictionary.Count; }
}
public bool IsReadOnly
{
get { return true; }
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
throw ReadOnlyException();
}
#endregion
#region IEnumerable<KeyValuePair<TKey,TValue>> Members
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return _dictionary.GetEnumerator();
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
private static Exception ReadOnlyException()
{
return new NotSupportedException("This dictionary is read-only");
}
}
回答by Jeff Yates
.NET 4.5
.NET 4.5
The .NET Framework 4.5 BCL introduces ReadOnlyDictionary<TKey, TValue>(source).
.NET Framework 4.5 BCL 引入ReadOnlyDictionary<TKey, TValue>(来源)。
As the .NET Framework 4.5 BCL doesn't include an AsReadOnlyfor dictionaries, you will need to write your own (if you want it). It would be something like the following, the simplicity of which perhaps highlights why it wasn't a priority for .NET 4.5.
由于 .NET Framework 4.5 BCL 不包含AsReadOnlyfor 字典,您需要自己编写(如果需要)。它将类似于以下内容,其简单性可能突出了为什么它不是 .NET 4.5 的优先事项。
public static ReadOnlyDictionary<TKey, TValue> AsReadOnly<TKey, TValue>(
this IDictionary<TKey, TValue> dictionary)
{
return new ReadOnlyDictionary<TKey, TValue>(dictionary);
}
.NET 4.0 and below
.NET 4.0 及以下
Prior to .NET 4.5, there is no .NET framework class that wraps a Dictionary<TKey, TValue>like the ReadOnlyCollectionwraps a List. However, it is not difficult to create one.
在 .NET 4.5 之前,没有Dictionary<TKey, TValue>像ReadOnlyCollection包装List那样包装 .NET 框架类。然而,创造一个并不困难。
Here is an example- there are many others if you Google for ReadOnlyDictionary.
这是一个示例- 如果您在Google 上搜索 ReadOnlyDictionary,还有许多其他示例。
回答by knocte
It was announced in the recent BUILD conferencethat since .NET 4.5, the interface System.Collections.Generic.IReadOnlyDictionary<TKey,TValue>is included. The proof is here(Mono) and here(Microsoft) ;)
在最近的BUILD 会议上宣布,从 .NET 4.5 开始,System.Collections.Generic.IReadOnlyDictionary<TKey,TValue>就包含了该接口。证明在这里(Mono) 和这里(Microsoft) ;)
Not sure if ReadOnlyDictionaryis included too, but at least with the interface it shouldn't be difficult to create now an implementation which exposes an official .NET generic interface :)
不确定是否ReadOnlyDictionary也包含在内,但至少对于接口来说,现在创建一个公开官方 .NET 通用接口的实现应该不难:)
回答by Dale Barnard
Feel free to use my simple wrapper. It does NOT implement IDictionary, so it doesn't have to throw exceptions at runtime for dictionary methods that would change the dictionary. Change methods simply aren't there. I made my own interface for it called IReadOnlyDictionary.
随意使用我的简单包装。它没有实现 IDictionary,因此它不必在运行时为会更改字典的字典方法抛出异常。根本就没有改变方法。我为它制作了自己的界面,称为 IReadOnlyDictionary。
public interface IReadOnlyDictionary<TKey, TValue> : IEnumerable
{
bool ContainsKey(TKey key);
ICollection<TKey> Keys { get; }
ICollection<TValue> Values { get; }
int Count { get; }
bool TryGetValue(TKey key, out TValue value);
TValue this[TKey key] { get; }
bool Contains(KeyValuePair<TKey, TValue> item);
void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex);
IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator();
}
public class ReadOnlyDictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue>
{
readonly IDictionary<TKey, TValue> _dictionary;
public ReadOnlyDictionary(IDictionary<TKey, TValue> dictionary)
{
_dictionary = dictionary;
}
public bool ContainsKey(TKey key) { return _dictionary.ContainsKey(key); }
public ICollection<TKey> Keys { get { return _dictionary.Keys; } }
public bool TryGetValue(TKey key, out TValue value) { return _dictionary.TryGetValue(key, out value); }
public ICollection<TValue> Values { get { return _dictionary.Values; } }
public TValue this[TKey key] { get { return _dictionary[key]; } }
public bool Contains(KeyValuePair<TKey, TValue> item) { return _dictionary.Contains(item); }
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) { _dictionary.CopyTo(array, arrayIndex); }
public int Count { get { return _dictionary.Count; } }
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { return _dictionary.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return _dictionary.GetEnumerator(); }
}
回答by Neal
IsReadOnly on IDictionary<TKey,TValue>is inherited from ICollection<T>(IDictionary<TKey,TValue>extends ICollection<T>as ICollection<KeyValuePair<TKey,TValue>>). It is not used or implemented in any way ( and is in fact "hidden" through the use of explicitly implementing the associated ICollection<T>members ).
IsReadOnly onIDictionary<TKey,TValue>继承自ICollection<T>( IDictionary<TKey,TValue>extends ICollection<T>as ICollection<KeyValuePair<TKey,TValue>>)。它没有以任何方式使用或实现(实际上是通过使用显式实现关联ICollection<T>成员来“隐藏”的)。
There are at least 3 ways I can see to solve the problem:
我可以看到至少有 3 种方法可以解决问题:
- Implement a custom read
only
IDictionary<TKey, TValue>and wrap / delegate to an inner dictionary as has been suggested - Return an
ICollection<KeyValuePair<TKey, TValue>>set as read only or anIEnumerable<KeyValuePair<TKey, TValue>>depending on the use of the value - Clone the dictionary using the copy
constructor
.ctor(IDictionary<TKey, TValue>)and return a copy - that way the user is free to do with it as they please and it does not impact on the state of the object hosting the source dictionary. Note that if the dictionary you are cloning contains reference types ( not strings as shown in the example ) you will need to do the copying "manually" and clone the reference types as well.
- 按照建议实现自定义只读
IDictionary<TKey, TValue>并包装/委托到内部字典 - 根据值的使用将
ICollection<KeyValuePair<TKey, TValue>>集合返回 为只读或一个IEnumerable<KeyValuePair<TKey, TValue>> - 使用复制构造函数克隆字典
.ctor(IDictionary<TKey, TValue>)并返回一个副本 - 这样用户就可以随意使用它,并且不会影响托管源字典的对象的状态。请注意,如果您要克隆的字典包含引用类型(不是示例中所示的字符串),您将需要“手动”进行复制并克隆引用类型。
As an aside; when exposing collections, aim to expose the smallest possible interface - in the example case it should be IDictionary as this allows you to vary the underlying implementation without breaking the public contract that the type exposes.
作为旁白; 在公开集合时,旨在公开尽可能小的接口 - 在示例情况下,它应该是 IDictionary,因为这允许您在不破坏类型公开的公共契约的情况下改变底层实现。
回答by Eamon Nerbonne
A read-only dictionary can to some extent be replaced by Func<TKey, TValue>- I usually use this in an API if I only want people performing lookups; it's simple, and in particular, it's simple to replace the backend should you ever wish to. It doesn't provide the list of keys, however; whether that matters depends on what you're doing.
只读字典在某种程度上可以被替换为Func<TKey, TValue>- 如果我只希望人们执行查找,我通常在 API 中使用它;它很简单,特别是,如果您愿意,可以很容易地更换后端。但是,它不提供密钥列表;这是否重要取决于你在做什么。
回答by wekempf
No, but it would be easy to roll your own. IDictionary does define an IsReadOnly property. Just wrap a Dictionary and throw a NotSupportedException from the appropriate methods.
不,但很容易推出自己的。IDictionary 确实定义了 IsReadOnly 属性。只需包装一个 Dictionary 并从适当的方法中抛出 NotSupportedException 。
回答by JaredPar
None available in the BCL. However I published a ReadOnlyDictionary (named ImmutableMap) in my BCL Extras Project
BCL 中没有。但是,我在BCL Extras 项目中发布了 ReadOnlyDictionary(名为 ImmutableMap)
In addition to being a fully immutable dictionary, it supports producing a proxy object which implements IDictionary and can be used in any place where IDictionary is taken. It will throw an exception whenever one of the mutating APIs are called
除了是一个完全不可变的字典之外,它还支持生成一个实现 IDictionary 的代理对象,并且可以在任何使用 IDictionary 的地方使用。每当调用其中一个变异 API 时,它都会抛出异常
void Example() {
var map = ImmutableMap.Create<int,string>();
map = map.Add(42,"foobar");
IDictionary<int,string> dictionary = CollectionUtility.ToIDictionary(map);
}
回答by Jason Coyne
You could create a class that only implements a partial implementation of the dictionary, and hides all the add/remove/set functions.
您可以创建一个仅实现字典的部分实现的类,并隐藏所有添加/删除/设置功能。
Use a dictionary internally that the external class passes all requests to.
在内部使用外部类将所有请求传递给的字典。
However, since your dictionary is likely holding reference types, there is no way you ca stop the user from setting values on the classes held by the dictionary (unless those classes themselves are read only)
但是,由于您的字典可能包含引用类型,因此您无法阻止用户在字典所包含的类上设置值(除非这些类本身是只读的)
回答by Jonas
I don't think there's an easy way of doing it...if your dictionary is part of a custom class, you could achieve it with an indexer:
我不认为有一种简单的方法可以做到……如果您的字典是自定义类的一部分,则可以使用索引器来实现:
public class MyClass
{
private Dictionary<string, string> _myDictionary;
public string this[string index]
{
get { return _myDictionary[index]; }
}
}

