为什么.net在引发KeyNotFound异常时为我们提供密钥(我怎么能得到它?)

时间:2009-05-20 08:28:57

标签: c# .net debugging exception dictionary

当您尝试访问不在Dictionary中的键时(例如),这是您获得的堆栈跟踪:

   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错误/重新抛出,但这会导致难以阅读的代码,与我的所有异常处理/日志记录相关。

4 个答案:

答案 0 :(得分:4)

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

我不知道异常不包含导致错误的字段的原因(可能因为它应该是ungeneric),但如果你认为你需要它就把它包起来。

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

答案 1 :(得分:1)

您可以使用ContainsKey或TryGetValue方法来验证字典是否包含密钥。

答案 2 :(得分:1)

正如达林指出的那样,你应该在你的代码中真正考虑到这一点,但如果出于某种原因,这是不可能的,你可以做到以下几点。

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

答案 3 :(得分:0)

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

然而,一旦你得到它们:

  

你如何处理这些例外?

由于您知道引发异常的位置,如果代码不是非常复杂,这允许您重现该错误(在调试中启用“Break on thrown exception”或英文版中调用的任何内容 - &gt ;此异常的异常对话框在那个时刻中断了),我通常会在几分钟内找出原因。调试器会告诉你在这种情况下密钥是什么,然后你的工作是找出密钥的原因(或者密钥不在字典中的原因),即使你通过错误报告得到了密钥名称。除非你有一些绝对一切都在的神词典,否则应该很容易找出问题所在。

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

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

但是,我正在为其他公司开发软件,而不是收缩包装软件,所以关于您与客户共度的时间可能会有所不同。