16

在我看来,我的很多调试时间都花在了追踪复杂语句中的空引用异常上。例如:

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.

这是严格的设计选择,旨在保护代码的匿名性,还是编译器设计中有令人信服的理由不将此信息包含在调试时异常中?

4

5 回答 5

11

异常是运行时的东西,变量是编译时的东西。

实际上,您示例中的变量是一个表达式。表达式并不总是简单的变量。在运行时,将评估表达式并在结果对象上调用方法。如果该表达式的值为null,则运行时将抛出NullReferenceException. 假设如下:

Dim a as New MyObject
Dim b as String = MyObject.GetNullValue().ToString()

GetNullValue()如果方法返回,运行时应该返回什么错误消息null

于 2009-02-23T18:34:21.383 回答
3

对于像 Java 这样编译为由 VM 解释的字节码的语言,假设您有一个X带有 field的类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,不能也不知道该空引用来自何处。

于 2009-02-23T19:23:00.487 回答
1

一种简单的方法来捕获这个以进行调试,以便在使用对象之前放置 Assert 语句,检查 null 并输出有意义的消息。

于 2009-02-23T18:36:26.290 回答
1

在发布版本中,变量名称从符号中删除,代码甚至可能被优化为没有变量的特定内存位置,而只是将引用保留在其中一个寄存器中(取决于变量使用的范围)。因此,可能无法从参考位置中扣除变量的名称。

在调试版本中,有更多关于变量的信息。但是,无论构建风格如何,异常对象都需要以相同的方式工作。因此,它以任何方式访问它可以访问的最少信息。

于 2009-02-23T18:37:40.173 回答
1

有几件事...

1)当您自己制定例外时,请记住这一点(如果您为此感到恼火,那么如果您为其他事情这样做,其他人也会对您感到恼火)。鉴于异常路径根本不应该是典型路径,因此花费时间使异常具有有用信息是非常值得的。

2)作为一般的编程实践者,采用这种风格,你会遇到更少的问题(是的,你的代码行数会更长,但你会节省很多时间):

a) 从不做 ab().c();做 x = ab(); xc(); (在单独的行上)您可以看到 a 为 null 或者 ab() 的返回为 null。

b) 永远不要将方法调用的返回值作为参数传递 - 始终传递变量。一个(富());应该是 x = foo(); 斧头); 这一个更适合调试并能够看到值。

我不知道为什么像 .net 和 Java 这样的环境不提供运行时版本,该版本确实包含有关此类异常的更多信息,例如超出范围的数组上的索引是什么,变量的名称何时它是空的,等等......

于 2009-02-23T18:46:51.913 回答