【问题标题】:Why doesn't .net provide us with the key when it raises a KeyNotFound Exception (and how can I get it?)为什么 .net 在引发 KeyNotFound 异常时不向我们提供密钥(我怎样才能得到它?)
【发布时间】:2010-10-27 13:45:59
【问题描述】:

当您尝试访问不在字典中的键(例如)时,您会得到以下堆栈跟踪:

   at System.ThrowHelper.ThrowKeyNotFoundException()
     at System.Collections.Generic.Dictionary`2.get_Item(TKey key)   
       .... .... (my own code stack trace)

像大多数人一样,我会在错误发生时记录这些错误,并尝试找出发生了什么。

我想要的两个关键信息是这发生在哪里(堆栈跟踪对此非常有帮助),以及导致异常的关键,它没有出现在任何地方。

要么我看的不正确(KeyNotFoundException 类包含一个“Data”成员,该成员始终为空,并且“Message”字段不包含键值),或者它没有包含在框架。

我无法想象 .net BCL 团队中没有人认为这将是一个有用的功能。我很想知道他们为什么不包括它。有什么好的理由不这样做吗?

您如何处理这些异常?我能想到的唯一选择是在 Dictionary 上使用自定义扩展方法,该方法将包装调用,捕获异常,并使用有关密钥的其他信息重新抛出它,但这对我不拥有的代码没有帮助,更改这样的“基本”功能感觉很糟糕。

你怎么看?


我知道this question,关于人们无法访问引发异常的方法的参数这一一般事实。我的问题特别与 KeyNotFoundException 有关。


编辑:我知道 TryGetValue 模式,当我希望我的集合可能不包含密钥时,我一直这样做。但是当它应该包含它时,我不测试,因为我更喜欢我的程序因异常而失败,以便我知道发生了一些意想不到的事情之前(即在应该插入密钥的时候)

我可以在我所有的字典访问中包装一个 try/catch/log 错误/重新抛出,但这会导致难以阅读的代码,并与我所有的异常处理/记录的东西混在一起。

【问题讨论】:

    标签: c# .net debugging exception dictionary


    【解决方案1】:

    您可以改用 ContainsKey 或 TryGetValue 方法来验证字典是否包含键。

    【讨论】:

      【解决方案2】:

      正如 darin 指出的那样,您确实应该在代码中考虑到这一点,但如果出于某种原因,这是不可能的,您可以执行以下操作。

      public object GetObjectFromDictionary(string key)
      {
          try
          {
              return MyDictionary[key];
          }
          catch (KeyNotFoundException kex)
          {
              throw new WrappedException("Failed To Find Key: " + key, kex);
          }
      }
      

      【讨论】:

        【解决方案3】:

        为什么要依赖(慢)异常?只需使用ContainsKeyTryGetValue 验证密钥是否存在?

        我不知道异常不包含导致错误的字段的原因(可能是因为它应该是非泛型的),但如果您认为需要它,只需将其包装起来。

        class ParameterizedKeyNotFoundException<T> : KeyNotFoundException {
            public T InvalidKey { get; private set; }
        
            public ParameterizedKeyNotFoundException(T InvalidKey) {
                this.InvalidKey = InvalidKey;
            }
        }
        
        static class Program {
        
            static TValue Get<TKey, TValue>(this IDictionary<TKey, TValue> Dict, TKey Key) {
                TValue res;
        
                if (Dict.TryGetValue(Key, out res))
                    return res;
        
                throw new ParameterizedKeyNotFoundException<TKey>(Key);
            }
        
            static void Main(string[] args) {
        
                var x = new Dictionary<string, int>();
        
                x.Add("foo", 42);
        
                try {
                    Console.WriteLine(x.Get("foo"));
                    Console.WriteLine(x.Get("bar"));
                }
                catch (ParameterizedKeyNotFoundException<string> e) {
                    Console.WriteLine("Invalid key: {0}", e.InvalidKey);
                }
        
                Console.ReadKey();
            }
        }
        

        【讨论】:

        【解决方案4】:

        我无法真正让您了解为什么没有在异常中包含密钥。正如 darin 所说,您应该始终使用 TryGetValue - 它使代码更易于阅读和维护,并且您不会得到 KeyNotFoundException(并且,当您使用它时,始终使用 TryParse 而不是 Parse,对于 int、double、DateTime ,无论如何,出于同样的原因)。

        但是,一旦你得到它们:

        你如何处理这些异常?

        由于您知道异常是在哪里引发的,如果代码不是非常复杂,这允许您重现错误(启用“抛出异常时中断”,或在英文版中调用的任何内容,在 Debugging->这个异常的异常对话框在那一刻中断),我通常会在几分钟内找出原因。调试器会告诉您在这种情况下的密钥是什么,然后您的工作就是找出为什么这是密钥(或为什么密钥不在字典中),即使您通过错误报告获得密钥名称也是如此。除非你有某种神字典,里面绝对有所有的东西,否则应该很容易找出问题所在。

        代码中不应该留下任何未处理的异常,所以遇到这样的事情应该是很少见的,然后我可以进一步调查一下。换句话说,我从不觉得需要从错误报告中获取密钥。

        当然,好吧,如果您无法重现错误,这对您没有帮助 - 然后我通常会将日志记录代码添加到失败区域并将该版本分发给遇到错误的客户(如果它更多比一位客户,通常可以使用客户的数据重现错误)。

        但是,我正在为其他公司开发软件,而不是收缩包装软件,因此对于您可以花在客户身上的时间而言,情况可能会有所不同。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-12-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多