【问题标题】:Inconsistent behaviour in Dictionary/IDictionaryDictionary/IDictionary 中的行为不一致
【发布时间】:2020-03-01 22:12:07
【问题描述】:

获取以下代码

        var dictionary = new Dictionary<string, int>
        {
            ["A"] = 1,
            ["B"] = 2,
            ["C"] = 3,
        };

        var dictionaryx = (IDictionary)dictionary;

        var x = dictionaryx["X"]; // return null
        var y = dictionary["Y"];  // throws KeyNotFoundException

有趣的是,通过索引器访问元素会导致xy 中的两种不同行为。 通常,接口应该允许访问相同的内部实现。但在这种情况下,情况就不同了。

这里有什么问题?

【问题讨论】:

    标签: c# .net dictionary


    【解决方案1】:

    这是因为Dictionary&lt;TKey, TValue&gt; 实现了IDictionary&lt;TKey, TValue&gt; 的通用版本和非通用版本(只是IDictionary 产生Object)。

    查看源码可以很容易看到indexer of the generic version在找不到key时显式抛出:

    public TValue this[TKey key]
    {
        get
        {
            int i = FindEntry(key);
            if (i >= 0) return entries[i].value;
            ThrowHelper.ThrowKeyNotFoundException(); // <-- this line
            return default(TValue);
        }
    }
    

    但是non-generic version 没有:

    object IDictionary.this[object key]
    {
        get
        {
            if (IsCompatibleKey(key))
            {
                int i = FindEntry((TKey)key);
                if (i >= 0)
                    return entries[i].value;
            }
            return null;
        }
    }
    

    然后,除非您将 Dictionary 显式转换为非通用版本,否则您将调用在找不到键时抛出的索引器。

    更新(根据您的评论):

    由于IDictionary(非泛型版本)是在.NET 1.0 中引入的(当时还不支持泛型),它使用和返回的唯一类型显然是System.Object(可以测试@987654332 @)。

    因此,在找不到密钥时不要扔是有意义的,因为可以简单地这样做:

    dict["nonExistingKey"] != null
    

    但是,一旦引入了通用 IDictionary&lt;TKey, TValue&gt;TValue 也可以设置为值类型(如 intDateTime),并且必须将上述模式调整为:

    var dict = new Dictionary<string, int>();
    dict["nonExistingKey"] != 0;
    dict["nonExistingKey"] != default(int);
    

    哪个不太流畅,不太标准,使用起来更麻烦。

    从“设计角度”来看,我认为他们决定保留 IDictionary 的向后兼容性,同时主动抛出通用版本。

    【讨论】:

    • 感谢您指出第二个显式实现(我在第一次浏览时没有看到)。我更多的是从设计的角度来证明这种行为的合理性,而不是代码在哪里或是谁编写的。但没有人回答“为什么”它在那里,除了“什么”在那里。我接受这个答案,因为无论如何这是最相关的答案。
    • @Sency,我用可能的解释更新了我的答案
    【解决方案2】:

    System.Collections.Generic.Dictionary&lt;K,V&gt; 实现了许多接口。

    它实现了System.Collections.Generic.IDictionary&lt;K,V&gt;,它指定使用 indexer 属性访问不存在的键应该抛出。

    它还实现了System.Collections.IDictionary,它指定使用 indexer 属性访问不存在的键应返回 null。

    Dictionary 类的默认索引器是第一个 - 来自 System.Collections.Generic.IDictionary&lt;K,V&gt;。当您转换为 IDictionary 时,您说您想要第二种行为。

    另外,请注意((IDictionary)dictionary)["key"] 返回一个对象,而dictionary["key"] 返回一个 int。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-09-24
      • 1970-01-01
      • 1970-01-01
      • 2010-09-24
      • 2010-12-08
      • 1970-01-01
      • 1970-01-01
      • 2011-04-24
      相关资源
      最近更新 更多