简体   繁体   English

字典/IDictionary 中的不一致行为

[英]Inconsistent behaviour in Dictionary/IDictionary

Take the following piece of code取下面一段代码

        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

It is interesting to see the element access through the indexer leads to two different behaviours in x and y .有趣的是,通过索引器访问元素会导致xy两种不同行为。 As in general, interfaces should give access to the same internal implementation.通常,接口应该允许访问相同的内部实现。 but in this case, it is different.但在这种情况下,情况就不一样了。

what is the catch here?这里有什么问题?

That's because Dictionary<TKey, TValue> is implementing both the generic version of IDictionary<TKey, TValue> and the non-generic version (just IDictionary that yields an Object ).这是因为Dictionary<TKey, TValue>正在实现IDictionary<TKey, TValue>的通用版本和非通用版本(只是IDictionary产生一个Object )。

Looking at the source, you can easily see that the indexer of the generic version explicitly throws when the key is not found:查看源代码,您可以轻松看到通用版本索引器在找不到键时显式抛出:

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

But the non-generic version does not:非通用版本不会:

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

Then, unless you explicitly cast your Dictionary to the non-generic version, you're calling the indexer that throws when the key is not found.然后,除非您显式地将Dictionary转换为非通用版本,否则您将调用在找不到键时抛出的索引器。

Update (as per your comment):更新(根据您的评论):

Since IDictionary (the non-generic version) was introduced in .NET 1.0 (when there was no support for generics yet) the only type it consumed and returned is obviously System.Object (which can be tested for null ).由于IDictionary (非泛型版本)是在 .NET 1.0 中引入的(当时还不支持泛型),它使用和返回的唯一类型显然是System.Object (可以测试null )。

Hence, it made sense not to throw when key is not found because one could simply do this:因此,在找不到密钥时不抛出是有意义的,因为可以简单地这样做:

dict["nonExistingKey"] != null

But, once the generic IDictionary<TKey, TValue> was introduced, the TValue can also be set to a value type (like int or DateTime ) and the above pattern would have to be adjusted to:但是,一旦引入了泛型IDictionary<TKey, TValue> ,也可以将TValue设置为值类型(如intDateTime ),并且必须将上述模式调整为:

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

Which is less fluent, less standard and more cumbersome to use.哪个更不流畅,更不标准,使用起来更麻烦。

From "design perspective", I presume they decided to preserve backward compatibility for IDictionary while proactively throwing for the generic version.从“设计角度”来看,我认为他们决定保留IDictionary向后兼容性,同时主动放弃通用版本。

System.Collections.Generic.Dictionary<K,V> implements many interfaces. System.Collections.Generic.Dictionary<K,V>实现了许多接口。

It implements System.Collections.Generic.IDictionary<K,V> , which specifies that access to non-existing keys using the indexer property should throw.它实现System.Collections.Generic.IDictionary<K,V> ,它指定使用索引器属性访问不存在的键应该抛出。

It also implements System.Collections.IDictionary , which specifies that access to non-existing keys using the indexer property should return null.它还实现System.Collections.IDictionary ,它指定使用 indexer 属性访问不存在的键应返回 null。

The default indexer of the Dictionary class is the first one - from System.Collections.Generic.IDictionary<K,V> . Dictionary 类的默认索引器是第一个 - 来自System.Collections.Generic.IDictionary<K,V> When you cast to IDictionary , you say you want the second behaviour.当您转换为IDictionary ,您说您想要第二种行为。

Also, note that ((IDictionary)dictionary)["key"] returns an object, while dictionary["key"] returns an int.另外,请注意((IDictionary)dictionary)["key"]返回一个对象,而dictionary["key"]返回一个 int。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM