【问题标题】:How to create a new Dictionary<,> from an IReadOnlyDictionary<,>?如何从 IReadOnlyDictionary<,> 创建一个新的 Dictionary<,>?
【发布时间】:2015-02-23 08:53:39
【问题描述】:

.NET 4.5 引入了方便的IReadOnlyDictionary&lt;TKey, TValue&gt; 接口。 Dictionary&lt;TKey, TValue&gt; is-an IReadOnlyDictionary&lt;TKey, TValue&gt;,所以我可以将前者传递到需要后者的任何地方。

不过,我不确定相反的方式:如何基于现有的IReadOnlyDictionary&lt;,&gt; 创建一个新的Dictionary&lt;,&gt;

  • Dictionary&lt;,&gt; 有几个采用 IDictionary&lt;,&gt; 的构造函数——但没有一个采用 IReadOnlyDictionary&lt;,&gt; 的构造函数。
  • IReadOnlyDictionary&lt;,&gt; 有一个扩展方法ToDictionary&lt;,&gt;()。但是,它继承自 IEnumerable&lt;&gt;。所以要使用它,我必须像这样传递两个完全多余的代表:readOnlyDict.ToDictionary(pair =&gt; pair.Key, pair =&gt; pair.Value)。丑!
  • 当然,我总是可以创建一个新的Dictionary&lt;,&gt;,遍历原始对并将它们复制过来。

在我看来,应该有一种简单的方法可以基于现有的IReadOnlyDictionary&lt;,&gt; 创建一个新的Dictionary&lt;,&gt;。我错过了什么吗?

编辑: 一些澄清。我不是在寻找将IReadOnlyDictionary&lt;,&gt; 视为Dictionary&lt;,&gt; 的神奇方法。我想创建一个新的Dictionary&lt;,&gt;,它从IReadOnlyDictionary&lt;,&gt; 复制其初始值。这个问题是一种冗长的提问方式:

Dictionary&lt;,&gt; 有一个方便的构造函数来从IDictionary&lt;,&gt; 复制初始值。为什么没有人采取IReadOnlyDictionary&lt;,&gt; 以及达到相同结果的最惯用方式是什么?

【问题讨论】:

标签: c# dictionary


【解决方案1】:

Dictionary&lt;TKey, TValue&gt; 能够实现IReadOnlyDictionary&lt;TKey, TValue&gt;,因为任何采用后者的代码都承诺不会修改字典,这是前者提供的功能的子集。

但考虑另一个方向。您从IReadOnlyDictionary&lt;TKey, TValue&gt; 开始,然后尝试将其变成普通的Dictionary&lt;TKey, TValue&gt;。现在,你把之前承诺不会修改的东西,变成了可以修改的东西。

显然,那是行不通的。

此外,您不能仅仅假设只读实现在修改时会抛出异常或其他东西(例如,运行时安全,即使不是编译时安全),因为如您所知,您总是可以获得来自可变实现的只读接口。

为了安全起见,您有两种选择:

  1. 只需将整个字典内容复制到一个新对象中即可。
  2. 将只读对象包装在IDictionary&lt;TKey, TValue&gt; 实现中,如果您尝试修改它,确实会引发异常。

请注意,后者实际上并没有提供您具体要求的内容。它很接近,但它不是Dictionary&lt;TKey, TValue&gt; 的实际实例。

在复制方面,您有很多选择。就个人而言,我认为ToDictionary() 本身就很好。但是如果你真的不喜欢冗长,你可以将它包装在一个扩展方法中:

public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(
    this IReadOnlyDictionary<TKey, TValue> dict)
{
    return dict.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
}


附录:
我突然想到我可能应该澄清一下:以上所有假设您正在尝试转换IReadOnlyDictionary&lt;TKey, TValue&gt;任意 实例。显然,如果您知道或至少怀疑您的IReadOnlyDictionary&lt;TKey, TValue&gt; 对象实际上是Dictionary&lt;TKey, TValue&gt; 的一个实例,您可以将或尝试(分别)直接转换为Dictionary&lt;TKey, TValue&gt;

【讨论】:

  • 嗯,可能会有不同名称的静态方法,因此解析不会成为问题。喜欢:Dictionary.CreateFromReadOnly 和 Dictionary.CreateFrom
【解决方案2】:

你可以这样做:

Dictionary<string, string> dictionary = readOnlyDictionary
                .Select(dict => dict).ToDictionary(pair => pair.Key, pair => pair.Value);

【讨论】:

    【解决方案3】:

    似乎IReadOnlyDictionary&lt;&gt; 是后来添加的,并且出于向后兼容性的原因,现有的类(例如Dictionary&lt;&gt;)没有通过new 方法进行改造以支持它。但是[需要引用]。

    对于它的价值,Microsoft's reference implementationDictionary 的构造函数本质上是

    Dictionary(IDictionary dictionary)
    : this(dictionary.Count) {
        foreach (KeyValuePair<TKey,TValue> pair in dictionary) {
                Add(pair.Key, pair.Value);
        }
    }
    

    Enumerable 本质上是

    Dictionary<> ToDictionary<>(this IEnumerable<> source, Func<> keySelector, Func<> elementSelector) 
    {
        Dictionary<> d = new Dictionary<>();
        foreach (TSource element in source) 
        d.Add(keySelector(element), elementSelector(element));
        return d;
    }
    

    所以区别似乎主要是对选择器代表的调用。

    因此,复制构造函数代码但采用IReadOnlyDictionary&lt;&gt; 的扩展方法,或多或少就像@Steve 所做的那样,似乎稍微简单一点,效率也稍微高一点...

    【讨论】:

      【解决方案4】:

      对于“为什么 C# 没有这个特性”,经常被引用的答案是设计、实现、记录、测试等都需要时间,尤其是在必须考虑与现有代码的源代码和二进制兼容性时帐户。

      直观地说,任何IDictionary&lt;,&gt; 也将是IReadOnlyDictionary&lt;,&gt;(后者是前者接口的子集),如果IReadOnlyDictionary&lt;,&gt; 从一开始就存在,它可能会以这种方式设置(与相关的构造函数将IReadOnlyDictionary&lt;,&gt; 作为其参数,并且由于继承而自动接受IDictionary&lt;,&gt;),但目前IDictionary&lt;,&gt;IReadOnlyDictionary&lt;,&gt; 是不相关的接口,它们恰好包含相同的功能。我只能假设它们不相关的原因是为了避免破坏现有的用户代码,这些代码执行IDictionary&lt;,&gt; 的显式接口实现。

      因此,采用IReadOnlyDictionary&lt;TKey, TValue&gt; 的新构造函数可能是一个新的不相关函数,它恰好包含与采用IDictionary&lt;TKey, TValue&gt; 的现有构造函数相同的代码,或者一个或其他构造函数需要使用包装器.

      我只能假设这并不比管道中其他可能的功能更重要...

      ...当我遇到同样的问题时,很难找到现有的问题(即便如此,它已经存在了 1.5 年,但没有回答所提出的问题),这表明他们优先考虑其他更重要的功能是正确的。

      在我自己的例子中,我有一个派生自 Dictionary&lt;MyEnum,double&gt; 的类(为了拥有一个也通过访问特定键实现接口的字典),并且想编写一个构造函数,例如:

      public MyDictionary(IReadOnlyDictionary<MyEnum, double> source) : base(source)
      { }
      

      相反,在尝试查看其他任何人遇到类似问题(并找到此问题)后,我写了类似于以下内容的内容:

      public MyDictionary(IReadOnlyDictionary<MyEnum, double> source)
      {
        foreach (var pair in source)
          base.Add(pair.Key, pair.Value);
      }
      

      在我们直接创建 Dictionary&lt;,&gt; 的情况下,至少有两个完全可读的选项 - 类似于上面的循环,或者 ToDictionary 扩展函数,原始问题仅仅因为“丑陋”而拒绝(我拒绝了,因为这意味着复制字典两次——一次在ToDictionary 调用中,一次在基类构造函数中)。

      我希望 Dictionary&lt;,&gt; 上的“缺失”构造函数在实现中同样微不足道,但他们还需要处理伴随任何新功能的所有其他相关的 palaver。 Dictionary&lt;,&gt;IDictionary&lt;,&gt; 以各种有趣的方式非常广泛地使用,即使是最简单和最无害的更改也使得破坏东西的可能性更大。

      编辑: 进一步思考......如果要实现“缺失”的构造函数,那么对于实现 both 的类应该调用哪个构造函数会变得模棱两可> 接口IDictionary&lt;,&gt;IReadOnlyDictionary&lt;,&gt;。最好的例子是 Dictionary 类本身。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-16
        • 1970-01-01
        • 1970-01-01
        • 2021-04-17
        • 2021-10-13
        • 2012-02-03
        相关资源
        最近更新 更多