【问题标题】:Make dictionary read only in C#在 C# 中使字典只读
【发布时间】:2016-12-29 10:01:00
【问题描述】:

我有一个Dictionary<string, List<string>> 并希望将该成员公开为只读。我看到我可以将其返回为 IReadOnlyDictionary<string, List<string>>,但我不知道如何将其返回为 IReadOnlyDictionary<string, IReadOnlyList<string>>

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

请注意,在这种情况下,简单地使用 IReadOnlyDictionary 并没有帮助,因为我也希望这些值也是只读的。看来这样做的唯一方法是构建另一个 IReadOnlyDictionary,并将 IReadOnlyList 添加到它们。

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

【问题讨论】:

标签: c# collections covariance


【解决方案1】:

这就像将整个字典引用转换为 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 package 添加到您的解决方案中,您就可以使用它们了:

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

详细了解Immutable Collections here

【讨论】:

  • @HamletHakobyan 谢谢!不可变对象很强大,学习它们是值得的。只读集合不是不可变的,OP 说过“我想要常量之类的东西......”
  • 不会“ToDictionary”,创建整个字典的副本吗?
  • @bpeikes 是的,您需要这样做,因为您无法通过显式或隐式转换将列表转换为只读列表。
  • 您可以转换为 IReadOnlyDictionary 但它可以转换回 Dictionary 并修改字典,因为实例本身没有改变。
【解决方案2】:

首先,您必须创建一个包含所需内容类型的新字典:

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

然后你可以只返回新字典,因为IReadOnlyDictionaryDictionary 的超类型。


为什么需要这样做?因为Dictionary&lt;T, A&gt; 不是Dictionary&lt;T, B&gt; 的超类型,即使 AB 的超类型。为什么?考虑以下示例:

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.

换句话说,TValueDictionary is not covariant。从面向对象的角度来看,协方差应该在字典的只读版本中是可能的,但是 .NET 框架中存在阻止这种情况的遗留问题(请参阅以“ UPDATE”在this question 了解详情)。

【讨论】:

  • 这只是因为TValue 不能是协变的,但在这种情况下,我相信List&lt;string&gt;IReadOnlyList&lt;string&gt;。不完全是AB
  • @MatíasFidemraizer:有什么不同?如果 A 是 B 的超类型,则 B 是 A。
  • Dictionary 不需要与 TValue 协变,**IReadOnlyDictionary 需要与 TValue 协变。
  • @Servy:没错,从 OO 的角度来看,这没问题。不幸的是,框架中似乎有一些遗留问题阻止了这种情况(请参阅lower half of this question 以获得解释)。
  • @Heinzi 那是IDictionary,不是IReadOnlyDictionary
【解决方案3】:

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

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

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

但马蒂亚斯的回答似乎更合适。

【讨论】:

  • 像字典这样的只读复杂类型仍然可以使用.Add().Remove()等方法进行更改
【解决方案4】:

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

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

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 将是最佳选择。

【讨论】:

    【解决方案5】:

    鉴于您专门寻找只读的Dictionary&lt;string, List&lt;string&gt;&gt;,您基本上是在寻找Lookup

    Dictionary 对象具有ToLookup() 扩展名。

    【讨论】:

    • 不完全是。阅读 Lookup 类的备注部分:A Lookup&lt;TKey,TElement&gt; resembles a Dictionary&lt;TKey,TValue&gt;. The difference is that a Dictionary&lt;TKey,TValue&gt; maps keys to single values, whereas a Lookup&lt;TKey,TElement&gt; maps keys to collections of values.
    • 是的,@MladenB.,但 OP 要求该值为 List&lt;string&gt;,因此查找工作完美。
    【解决方案6】:

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

    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 始终保持最新状态

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

    【讨论】:

      猜你喜欢
      • 2015-09-23
      • 1970-01-01
      • 1970-01-01
      • 2013-10-02
      • 2012-09-16
      • 1970-01-01
      • 2016-09-20
      • 2017-06-16
      • 1970-01-01
      相关资源
      最近更新 更多