使字典在 C# 中只读

问题描述 投票:0回答:7

我有一个

Dictionary<string, List<string>>
并且想将该成员公开为只读。我看到我可以将它作为
IReadOnlyDictionary<string, List<string>>
退回,但我无法弄清楚如何将它作为
IReadOnlyDictionary<string, IReadOnlyList<string>>
退回。

有没有办法做到这一点?在 C++ 中,我只是使用 const,但 C# 没有。

请注意,在这种情况下,简单地使用

IReadOnlyDictionary
没有帮助,因为我也希望这些值也是只读的。看起来唯一的方法是构建另一个 IReadOnlyDictionary,并将 IReadOnlyList 添加到它们。

另一个我不会感到兴奋的选择是创建实现接口 IReadOnlyDictionary> 的包装器,并让它保存原始实例的副本,但这似乎有点过分了。

c# dictionary collections covariance
7个回答
37
投票

这就像将整个字典引用转换为

IReadOnlyDictionary<string, IReadOnlyList<string>>
一样简单,因为
Dictionary<TKey, TValue>
实现了
IReadOnlyDictionary<TKey, TValue>
.

顺便说一句,你不能那样做,因为你想要

List<string>
值作为
IReadOnlyList<string>
.

所以你需要这样的东西:

var readOnlyDict = (IReadOnlyDictionary<string, IReadOnlyList<string>>)dict
                        .ToDictionary(pair => pair.Key, pair => pair.Value.AsReadOnly());

不可变字典

这只是一个建议,但如果您正在寻找不可变的字典,请将

System.Collections.Immutable
NuGet 包 添加到您的解决方案中,您将能够使用它们:

// ImmutableDictionary<string, ImmutableList<string>>
var immutableDict = dict
           .ToImmutableDictionary(pair => pair.Key, pair => pair.Value.ToImmutableList());

在此处了解有关不可变集合的更多信息.


8
投票

鉴于您专门寻找只读

Dictionary<string, List<string>>
这一事实,您基本上正在寻找Lookup.

Dictionary 对象具有

ToLookup()
扩展名。


4
投票

我遇到了同样的问题。我通过以下方式解决了它。

List<string> list = new List<string>();

Dictionary<string, IReadOnlyCollection<string>> dic = new Dictionary<string, IReadOnlyCollection<string>>();

IReadOnlyDictionary<string, IReadOnlyCollection<string>> dicRo = new ReadOnlyDictionary<string, IReadOnlyCollection<string>>(dic);

 list.Add("Test1");

 dic["T"] = list.AsReadOnly();

 ist.Add("Test2");

这有积极的效果,你

  • 仍然可以将项目添加到列表中
  • 仍然可以添加项目到字典
  • 无法编辑 ReadOnlyDictionary
  • 无法编辑 ReadOnlyCollection
  • 无法将其转换为字典
  • 无法将其放入列表
  • 让您的 ReadOnlyDictionary 始终保持最新状态

也许这会对某人有所帮助。


3
投票

首先,您必须创建一个具有所需内容类型的新词典:

var dicWithReadOnlyList = dic.ToDictionary(
    kv => kv.Key,
    kv => kv.Value.AsReadOnly());

然后你可以只返回新字典,因为

IReadOnlyDictionary
Dictionary
的超类型。


为什么你需要这样做?因为

Dictionary<T, A>
不是
Dictionary<T, B>
的超类型,即使
A
B
的超类型。为什么?考虑以下示例:

var dic = new Dictionary<T, B>();
Dictionary<T, A> dic2 = dic;      // Imagine this were possible...

dic2.Add(someT, someA);           // ...then we'd have a type violation here, since
                                  // dic2 = dic requires some B as the value.

换句话说,

TValue
中的
Dictionary不是协变的。从面向对象的角度来看,协变应该在字典的只读版本中是可能的,但是.NET框架中存在遗留问题阻止了这一点(参见this中以“UPDATE”开头的部分问题了解详情)。


0
投票

如果你想返回一个只读字典,但仍然能够在你的类中改变字典和列表,你可以使用转换来取回列表类型。

这个例子有点做作,但展示了它是如何工作的。

public class MyClass
{
    Dictionary<string, IReadOnlyList<string>> _dictionary;
    public IReadOnlyDictionary<string, IReadOnlyList<string>> Dictionary { get { return _dictionary; } }

    public MyClass()
    {
        _dictionary = new Dictionary<string, IReadOnlyList<string>>();
    }

    public void AddItem(string item)
    {
        IReadOnlyList<string> readOnlyList = null;
        List<string> list = null;
        if (!_dictionary.TryGetValue(item, out readOnlyList))
        {
            list = new List<string>();
            _dictionary.Add(item, list);
        }
        else
            list = readOnlyList as List<string>;
        list.Add(item);
    }
}

如果您的目标是让属性不可变,那么使用 ReadOnlyDictionary 将是最佳选择。


0
投票

不是直接回答问题,而是.Net 8介绍FrozenDictionary.

新的

System.Collections.Frozen
命名空间包括集合类型
FrozenDictionary<TKey,TValue>
FrozenSet<T>
。一旦创建了集合,这些类型就不允许对键和值进行任何更改。该要求允许更快的读取操作(例如,
TryGetValue()
)。这些类型对于首次使用时填充然后在长期服务期间持续存在的集合特别有用,例如:


-2
投票

https://msdn.microsoft.com/en-us/library/acdd6hb7.aspx

您可以使用它来将对象公开为只读。

你也可以使用属性获取;放;并且只允许公开。

但Matias的回答似乎更合适。

© www.soinside.com 2019 - 2024. All rights reserved.