如何创建新词典<,>来自IReadOnlyDictionary<,>?

时间:2015-02-23 08:53:40

标签: c# dictionary

.NET 4.5引入了方便的IReadOnlyDictionary<TKey, TValue>接口。 Dictionary<TKey, TValue> 是一个 IReadOnlyDictionary<TKey, TValue>,所以我可以通过前者而不需要后者。

我不确定相反的方式:如何基于现有的Dictionary<,>创建新的IReadOnlyDictionary<,>

  • Dictionary<,>有几个带IDictionary<,>的构造函数 - 但没有一个带IReadOnlyDictionary<,>
  • IReadOnlyDictionary<,>有一个扩展方法ToDictionary<,>()。但是,这是继承自IEnumerable<>。所以要使用它,我必须传递两个完全冗余的代表:readOnlyDict.ToDictionary(pair => pair.Key, pair => pair.Value)。太丑了!
  • 当然,我总是可以创建一个新的Dictionary<,>,迭代原始对并复制它们。

在我看来,基于现有的Dictionary<,>创建新的IReadOnlyDictionary<,>应该有一个微不足道的方法。我错过了什么吗?

修改:一些澄清。我不是在寻找一种将IReadOnlyDictionary<,>视为Dictionary<,>的神奇方法。我想创建一个新的Dictionary<,>,从IReadOnlyDictionary<,>复制其初始值。这个问题是一种冗长的问题:

Dictionary<,>有一个便利构造函数来复制IDictionary<,>的初始值。为什么没有人采用IReadOnlyDictionary<,>以及获得相同结果的最惯用方式是什么?

4 个答案:

答案 0 :(得分:12)

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

但考虑另一个方向。您从IReadOnlyDictionary<TKey, TValue>开始,然后尝试将其转换为常规Dictionary<TKey, TValue>。现在,您已经采取了以前承诺不会被修改的内容,并将其转换为可以修改的内容。

显然,这只是不会做。

此外,您不能仅仅假设只读实现会在修改时抛出异常(例如运行时安全,即使不是编译时安全),因为,如您所知,您始终可以从可变实现中获取只读接口。

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

  1. 只需将整个字典内容复制到新对象中。
  2. IDictionary<TKey, TValue>实现中包装只读对象,如果您尝试修改它, 会抛出异常。
  3. 请注意,后者实际上并没有为您提供您特别要求的内容。它很接近,但它不是Dictionary<TKey, TValue>的实际实例。

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

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


    附录:
    在我看来,我应该澄清一下:以上都假设您正在尝试转换IReadOnlyDictionary<TKey, TValue>任意实例。显然,如果您知道或至少怀疑您的IReadOnlyDictionary<TKey, TValue>对象实际上是Dictionary<TKey, TValue>的实例,您可以(或分别)直接投射或尝试投射到Dictionary<TKey, TValue>

答案 1 :(得分:1)

似乎后来添加了IReadOnlyDictionary<>,并且由于向后兼容性原因,现有的类(例如Dictionary<>)未进行改装以支持 new 方法。 [引证需要],但是。

对于它的价值,Dictionary的{​​{1}}构造函数Microsoft's reference implementation基本上是

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

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

所以一个扩展方法复制构造函数的代码,但是@ {1}更多或更少的IReadOnlyDictionary<>似乎更简单,更有效......

答案 2 :(得分:1)

您可以这样做:

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

答案 3 :(得分:0)

任何&#34;为什么没有C#的经常被引用的答案都有这个功能&#34;是设计,实现,记录,测试等需要时间,特别是在必须考虑源代码和二进制与现有代码的兼容性时。

直观地说,任何IDictionary<,>也都是IReadOnlyDictionary<,>(后者是前者界面的一个子集),也许IReadOnlyDictionary<,>从一开始就存在将以这种方式设置(相关构造函数将IReadOnlyDictionary<,>作为其参数,并由于继承而自动接受IDictionary<,>),但事情是IDictionary<,>IReadOnlyDictionary<,>是不相关的接口恰好包含相同的功能。我只能假设他们不相关的原因是为了避免破坏现有的用户代码来执行IDictionary<,>的显式接口实现。

因此,采用IReadOnlyDictionary<TKey, TValue>的新构造函数将是一个新的无关函数,恰好包含与IDictionary<TKey, TValue>的现有构造函数相同的代码,或者需要一个或其他构造函数使用包装器。

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

...而且当我遇到同样的问题时,很难找到现有的问题(即使那时它已经存在了1。5年而没有回答被问到的问题),表明他们是正确的优先考虑其他更重要的功能。

在我自己的情况下,我有一个派生自Dictionary<MyEnum,double>的类(为了拥有一个通过访问特定键来实现接口的字典),并想编写一个如下构造函数:

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<,>,则至少有两个完全可读的选项 - 类似于上述的循环,或原始问题拒绝的ToDictionary扩展函数仅仅因为丑陋而且#34; (我拒绝它,因为它意味着复制字典两次 - 一次在ToDictionary调用,一次在基类构造函数中。)

我希望&#34;失踪&#34; Dictionary<,>上的构造函数在实现中同样微不足道,但是他们还需要处理伴随任何新功能的所有其他关联的palaver。 Dictionary<,>IDictionary<,>非常广泛地用于各种有趣的方式,即使最简单,最无害的变化,也有可能破坏它们。

编辑:进一步思考......如果&#34;缺失&#34;如果要实现构造函数,那么应该为一个实现接口IDictionary<,>IReadOnlyDictionary<,> 的类调用构造函数。主要的例子是Dictionary类本身。