在我看来,我的很多调试时间花在追踪复杂语句中的空引用异常上。例如:
For Each game As IHomeGame in _GamesToOpen.GetIterator()
为什么,当我得到NullReferenceException时,我是否可以获取堆栈跟踪中的行号,但不能获取等于null的对象的名称。换句话说,为什么:
Object reference not set to an instance of an object.
而不是
_GamesToOpen is not set to an instance of an object.
或
Anonymous object returned by _GamesToOpen.GetIterator() is null.
或
game was set to null.
这是一个严格的设计选择,是为了保护代码的匿名性,还是编译器设计中有一个令人信服的理由不在调试时异常中包含这些信息?
答案 0 :(得分:11)
异常是运行时事物,变量是编译时间。
实际上,示例中的变量是表达式。表达式并不总是简单的变量。在运行时,将计算表达式,并在结果对象上调用该方法。如果该表达式的值为null
,则运行时将抛出NullReferenceException
。假设如下:
Dim a as New MyObject
Dim b as String = MyObject.GetNullValue().ToString()
如果GetNullValue()
方法返回null
?
答案 1 :(得分:2)
对于编译为由VM解释的字节码的Java之类的语言,假设您有一个字段X
,其字段为x
,其值为null
参考。如果你写
x.foo()
字节码可能如下所示:
push Xref >> top of stack is ref to instance of X with X.x = null
getField x >> pops Xref, pushes 'null' on the stack
invokeMethod foo >> pops 'null' -> runtime exception
关键是需要对堆栈执行非空引用的操作(如示例中的invokeMethod)不能也不知道该null引用的来源。
答案 2 :(得分:1)
一种简单的方法来捕获它以便在使用之前调试以放置Assert语句 一个对象,检查null并输出有意义的消息。
答案 3 :(得分:1)
在发布版本中,变量名称从符号中删除,代码甚至可能被优化为没有变量的特定内存位置,但只是将引用保留在其中一个寄存器中(取决于变量用法)。因此,可能无法从参考位置中扣除变量的名称。
在调试版本中,有更多关于变量的信息。但是,异常对象需要以相同的方式工作,而不管构建风格如何。因此,它会对任何风格的最小信息起作用。
答案 4 :(得分:1)
有几件事......
1)当你做出自己的例外情况时,请记住这一点(如果你因此而烦恼,如果你为别人做了其他事,别人会对你感到恼火)。鉴于异常路径根本不应该是典型路径,因此使用异常所花费的时间非常有用。
2)作为一般编程人员采用这种风格,你会遇到更少的问题(是的,你的代码在行数方面会更长,但你会节省很多时间):
a)永远不要做a.b()。c(); do x = a.b(); x.c(); (在不同的行上)你可以看到a为null或者a.b()的返回为null。
b)永远不会将方法调用的返回作为参数传递 - 始终传递变量。一个(FOO());应该是x = foo();斧头);这个更适合调试并且能够看到值。
我不知道为什么像.net和Java这样的环境不提供运行时版本,它确实有关于这些异常的更多信息,例如索引在数组上的界限是什么,名称是变量为null时等等......