【问题标题】:ConcurrentDictionary cast to IDictionary, indexer for missing key no longer throws exceptionConcurrentDictionary 强制转换为 IDictionary,缺少键的索引器不再抛出异常
【发布时间】:2017-10-04 04:51:50
【问题描述】:

我今天在进行一些单元测试时发现了一些令人惊讶的行为。如果您将 .NET ConcurrentDictionary<K, V> 实例(尚未使用标准泛型 Dictionary<K, V> 进行测试)转换为 IDictionary,然后使用索引器,它不再引发缺少键异常:

[TestMethod]
public void NotInDictionaryNative()
{
    var dict = new ConcurrentDictionary<string, int>();
    dict["abc"] = 3;
    dict["def"] = 4;

    Assert.IsTrue( ( (IDictionary)dict )["q"] == null );
}

这个单元测试通过了!但是,如果你取出演员表,它会失败。这似乎是一种危险的行为:显式实现不应该与常规公共索引器有太大差异,不是吗?

那么,如果我拥有的唯一已知类型是IDictionary,我如何检查是否存在一个密钥,而不是遍历所有密钥并进行比较?

【问题讨论】:

  • 这是IDictionary.Item 的预期行为 - 请参阅docs
  • 我是否遗漏了什么,或者有什么原因你不能使用IDictionary.ContainsKey
  • @JohnH,不幸的是,IDictionary 上没有定义这样的方法。 Contains(object key) 方法检查给定值是否存在而不是键。
  • @BrucePierson 您没有使用预期的IDictionary&lt;T,V&gt; 接口,而是使用返回对象的非常旧的IDictionary。这是一个非常不寻常的演员阵容
  • IDictionary<TKey,TValue> 接口确实抛出异常。更旧的(.NET 1.0)IDictionary 没有。只是不要使用IDictionary

标签: c# .net dictionary exception


【解决方案1】:

来自https://msdn.microsoft.com/en-us/library/system.collections.idictionary.item(v=vs.110).aspx

具有指定键的元素,如果键不存在,则为 null。

所以这是预期的行为。

这似乎是一种危险的行为:显式实现不应与常规公共索引器有太大差异,不是吗?

除非有非常充分的理由不这样做(真的足以让文档被认为是错误的),否则应该这样做。

那么,如果我唯一已知的类型是 IDictionary,我如何检查是否存在一个键,而不是遍历所有键并进行比较?

IDictionary.Contains 虽然这是一个麻烦(并且是多线程场景中的竞争),这就是为什么在 2.0 中引入了 KeyNotFoundExceptionIDictionary&lt;TKey, TValue&gt;.TryGetValue

缺少 TryGetValue 方法意味着在 1.1 中,对 IDictionary 进行测试的唯一方法是知道 null 永远不会被添加在那里。这远非理想,因此通用等效项改变了这一点,但更新 IDictionary 以匹配已为时已晚,因为所有现有用途都会被破坏。

【讨论】:

  • 谢谢乔恩。我通过查看合同看到“如果密钥不存在则为空”。所以我猜MS别无选择,只能为显式实现提供索引器的不同实现。我已经用( (dynamic)rhs ).ContainsKey( lhs ) 解决了我的问题。
  • IDictionary.Contains 应该也能正常工作,并且没有通过dynamic 的开销。如果另一个线程可以在包含检查和基于结果的操作之间更新它,则任何一种方式都有竞争条件。
  • 你当然是对的。谢谢。这是漫长的一周。我想就像新的索引器行为优于旧的索引器行为一样,ContainsKey 优于Contains,并且我阅读旧的Contains 方法来检查值而不是键的存在。我的错,再次感谢。
【解决方案2】:

接口是合同,这些合同可以包含成员将或不会抛出的异常。 IDictionary.Item 合约没有说实现者会抛出 keynotfound 异常,所以实现者不应该抛出它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-27
    • 1970-01-01
    • 2013-10-27
    • 1970-01-01
    • 2011-07-01
    • 2022-06-15
    相关资源
    最近更新 更多