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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/35002/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 09:15:24  来源:igfitidea点击:

Does C# have a way of giving me an immutable Dictionary?

提问by serg10

Is there anything built into the core C# libraries that can give me an immutable Dictionary?

核心 C# 库中是否有任何可以为我提供不可变字典的内容?

Something along the lines of Java's:

类似于Java 的内容

Collections.unmodifiableMap(myMap);

And just to clarify, I am not looking to stop the keys / values themselves from being changed, just the structure of the Dictionary. I want something that fails fast and loud if any of IDictionary's mutator methods are called (Add, Remove, Clear).

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

采纳答案by dbkk

No, but a wrapper is rather trivial:

不,但包装器相当简单:

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();
    }
}

Obviously, you can change the this[] setter above if you want to allow modifying values.

显然,如果您想允许修改值,您可以更改上面的 this[] 设置器。

回答by Kevin Dente

I don't think so. There is a way to create a read-only List and read only Collection, but I don't think there's a built in read only Dictionary. System.ServiceModel has a ReadOnlyDictinoary implementation, but its internal. Probably wouldn't be too hard to copy it though, using Reflector, or to simply create your own from scratch. It basically wraps an Dictionary and throws when a mutator is called.

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

回答by Scott Dorman

"Out of the box" there is not a way to do this. You can create one by deriving your own Dictionary class and implementing the restrictions you need.

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

回答by chakrit

One workaround might be, throw a new list of KeyValuePair from the Dictionary to keep the original unmodified.

一种解决方法可能是,从字典中抛出一个新的 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;

This way the original dictionary won't get modified.

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

回答by Olmo

I've found an implementation of an Inmutable (not READONLY) implementation of a AVLTree for C# here.

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

An AVL tree has logarithmic (not constant) cost on each operation, but stills fast.

AVL 树在每个操作上都有对数(不是常数)成本,但仍然很快。

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

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

回答by Sarah Vessels

Adding onto dbkk's answer, I wanted to be able to use an object initializer when first creating my ReadOnlyDictionary. I made the following modifications:

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

Now I can use the class like so:

现在我可以像这样使用这个类:

ReadOnlyDictionary<string, string> Fields =
    new ReadOnlyDictionary<string, string>(2)
        {
            {"hey", "now"},
            {"you", "there"}
        };

回答by Sarah Vessels

Since Linq, there is a generic interface ILookup. Read more in MSDN.

自 Linq 以来,有一个通用接口ILookup。在MSDN 中阅读更多内容。

Therefore, To simply get immutable dictionary you may call:

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

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

回答by SoftwareRockstar

There's also another alternative as I have described at:

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

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

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

Essentially it's a subclass of ReadOnlyCollection>, which gets the work done in a more elegant manner. Elegant in the sense that it has compile-time support for making the Dictionary read-only rather than throwing exceptions from methods that modify the items within it.

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

回答by David Moles

The open-source PowerCollectionslibrary includes a read-only dictionary wrapper (as well as read-only wrappers for pretty much everything else), accessible via a static ReadOnly()method on the Algorithmsclass.

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