当您尝试访问不在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错误/重新抛出,但这会导致难以阅读的代码,与我的所有异常处理/日志记录相关。
答案 0 :(得分:4)
为什么你依赖(慢)异常?只需验证该密钥是否存在ContainsKey
或TryGetValue
?
我不知道异常不包含导致错误的字段的原因(可能因为它应该是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,这对你没有帮助 - 我通常会将日志记录代码添加到失败区域,并将该版本交给遇到该错误的客户(如果它更多与一个客户相比,通常可以使用客户的数据重现错误。
但是,我正在为其他公司开发软件,而不是收缩包装软件,所以关于您与客户共度的时间可能会有所不同。