73

核心 C# 库中是否有任何东西可以给我一个不可变的字典?

类似于Java 的东西:

Collections.unmodifiableMap(myMap);

只是为了澄清,我不想阻止键/值本身被改变,只是字典的结构。Add, Remove, Clear如果任何 IDictionary 的 mutator 方法被调用( ),我想要一些快速而响亮的失败。

4

14 回答 14

51

不,但是包装器相当简单:

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[] 设置器。

于 2008-08-30T03:56:02.440 回答
16

随着 .NET 4.5 的发布,有一个新的ReadOnlyDictionary类。您只需将 an 传递IDictionary给构造函数即可创建不可变字典。

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

于 2012-09-17T16:06:31.143 回答
3

添加到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"}
        };
于 2009-07-09T15:33:25.800 回答
3

开源PowerCollections库包括一个只读字典包装器(以及几乎所有其他东西的只读包装器),可通过类ReadOnly()上的静态方法访问Algorithms

于 2011-06-24T16:48:47.320 回答
3

我知道这是一个非常古老的问题,但我不知何故在 2020 年找到了它,所以我想可能值得注意的是,现在有一种方法可以创建不可变字典:

https://docs.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();
    }
}
于 2020-04-07T22:02:16.313 回答
2

我不这么认为。有一种方法可以创建只读列表和只读集合,但我认为没有内置的只读字典。System.ServiceModel 有一个 ReadOnlyDictinoary 实现,但它是内部的。不过,使用 Reflector 或简单地从头开始创建自己的内容可能不会太难复制。它基本上包装了一个 Dictionary 并在调用 mutator 时抛出。

于 2008-08-29T19:02:09.180 回答
2

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

这样原始字典就不会被修改。

于 2008-08-29T19:44:59.500 回答
1

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

于 2008-08-29T19:24:53.307 回答
1

我在这里找到了用于 C# 的 AVLTree 的不可变(非只读)实现的实现。

AVL 树在每个操作上都有对数(非恒定)成本,但仍然很快。

http://csharpfeeds.com/post/7512/Immutability_in_Csharp_Part_Nine_Academic_Plus_my_AVL_tree_implementation.aspx

于 2009-02-02T14:21:00.920 回答
1

你可以尝试这样的事情:

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");
于 2011-12-21T04:49:16.447 回答
1

一般来说,最好不要一开始就传递任何字典(如果你不需要的话)。

相反 - 创建一个具有不提供任何修改字典(它包装)的方法的接口的域对象。而是提供所需的 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...};
}
于 2013-10-15T17:45:35.893 回答
0

从 Linq 开始,就有了一个通用接口ILookup在MSDN中阅读更多内容。

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

using System.Linq;
// (...)
var dictionary = new Dictionary<string, object>();
// (...)
var read_only = dictionary.ToLookup(kv => kv.Key, kv => kv.Value);
于 2009-09-15T17:49:50.657 回答
-1

正如我所描述的,还有另一种选择:

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

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

于 2010-10-30T05:29:50.340 回答