C# 无用异常(NullReferenceException、KeyNotFoundException)

C# 无用异常(NullReferenceException、KeyNotFoundException),c#,exception,nullreferenceexception,keynotfoundexception,C#,Exception,Nullreferenceexception,Keynotfoundexception,我觉得C#异常非常烦人,因为它们提供的信息太少了。这是什么原因 NullReferenceException或KeyNotFoundException很难调试,有时您无法在stacktrace中获得行号。为什么异常本身不能提供更多信息 例如: private Dictionary<string, object> Properties = null; public void Process(string key) { var item = this.Properties[k

我觉得C#异常非常烦人,因为它们提供的信息太少了。这是什么原因

NullReferenceException或KeyNotFoundException很难调试,有时您无法在stacktrace中获得行号。为什么异常本身不能提供更多信息

例如:

private Dictionary<string, object> Properties = null;

public void Process(string key)
{ 
    var item = this.Properties[key];
    ....
}
private Dictionary Properties=null;
公共作废进程(字符串键)
{ 
var item=this.Properties[key];
....
}
当“Properties”为null时,我得到一个NullReferenceException:

“System.NullReferenceException:对象引用未设置为对象的实例”

为什么我没有得到:

“System.NullReferenceException:对象引用“属性”未设置为对象的实例”

这将更有用,而且CLR确实知道哪个引用为null

当我传递一个不存在的密钥时也是一样,例如进程(“虚拟”):

“System.Collections.Generic.KeyNotFoundException:字典中不存在给定的键”

为什么我没有得到:

“System.Collections.Generic.KeyNotFoundException:字典中不存在键'dummy'

CLR知道传递了哪个密钥但没有找到

我尝试在生产环境中调试此类错误(传递非法密钥) 并使代码更加健壮,如:

private Dictionary<string, object> Properties = null;

public void Process(string key)
{ 
  if (this.Properties != null)
  {
    if (this.Properties.ContainsKey(key))
    {
        var item = this.Properties[key];
        ...
    }
    else
    {
        throw new KeyNotFoundException(string.Format("The key '{0}' was not found.", key));
    }
  }
  else
  {
    throw new NullReferenceException(string.Format("The object 'Properties' is null."));
  }
}
private Dictionary Properties=null;
公共作废进程(字符串键)
{ 
如果(this.Properties!=null)
{
if(this.Properties.ContainsKey(key))
{
var item=this.Properties[key];
...
}
其他的
{
抛出新的KeyNotFoundException(string.Format(“找不到键“{0}”,key));
}
}
其他的
{
抛出新的NullReferenceException(string.Format(“对象“属性”为null”);
}
}

但是为什么我必须这样做,通常CLR可以详细地告诉我出了什么问题。我无法像这样包装所有代码段,以便在发生异常时获得更多信息。

对于
KeyNotFoundException
s,您可以创建自己的字典类,该类抛出更多有意义的消息(我建议您尝试扩展
KeyNotFoundException
并抛出该类),或者您可以使用并抛出一个有意义的异常

>代码> Null ReavigyExist< /Cord>一个更复杂:假设CLR知道这个东西被称为“代码>属性”,但它不是那么简单:考虑<代码>。获取GATE(ABC).DOMEMETHONSE()/CODE >,其中<代码> GATE(ABC)返回NULL。什么是空的对象?它没有名字。有时,特别是在版本优化的代码中,会生成变量名或其他可能为null的内容


您应该使用测试用例、断点和其他调试模式代码进行调试,不要期望总是能够获得足够好的异常消息来调试生产代码。例如,即使您知道密钥
“dummy”
已传入,您也可能没有足够的信息来了解该密钥损坏或传入的原因。

对于
KeyNotFoundException
s,您可以创建自己的字典类来抛出更有意义的消息(我建议您尝试扩展
KeyNotFoundException
并抛出该异常),或者您可以使用并抛出一个有意义的异常

<> >代码> Null ReavigyExist< /Cord>一个更复杂:假设CLR知道这个东西被称为“代码>属性”,但它不是那么简单:考虑<代码>。返回null。什么是null对象?它没有名称。有时,特别是在版本优化的代码中,会生成可能为null的变量或其他对象的名称


您应该使用测试用例、断点和其他调试模式代码进行调试,不要期望您总能获得足够好的异常消息来调试生产代码。例如,即使您知道键
“dummy”
传入时,您可能没有足够的信息来知道该密钥为何损坏或传入。

然后您应该添加特殊处理以获取此信息。请记住,所有操作都要付出一定的代价。您进行的异常处理越臃肿(首先不应该发生这种情况)-对于每一个这样的案例,框架必须越大


你真的应该考虑这些“最后机会例外”。因为如果编码正确,您将永远看不到它们。在异常消息和提供的堆栈跟踪之间-这对于您的应用程序来说应该是足够的信息。

那么您应该添加特殊处理来获取此信息。请记住,每件事都要付出一定的代价。异常处理越臃肿(这本来就不应该发生)-对于每一个这样的案例,框架必须越大


你真的应该考虑这些“最后机会例外”。因为如果编码正确,您将永远看不到它们。在异常消息和提供的堆栈跟踪之间,这应该是足够的信息供您参考。

检查null或是否缺少密钥的责任在您身上,开发人员。这是因为优化;如果默认异常更详细就像您所希望的那样,编译器将无法高效地进行优化


这是一种权衡。添加null检查可能会让人恼火,但这是语言的设计方式。

检查null或是否缺少密钥的责任在您身上,开发人员。这是因为优化;如果默认异常像您希望的那样更详细,那么编译器将无法进行优化效率高

这是一种折衷。添加空检查可能会很烦人,但这就是语言的设计方式