Why can't a null-reference exception name the

2020-02-10 15:08发布

问题:

It seems to me that a lot of my debugging time is spent chasing down null-reference exceptions in complex statements. For instance:

For Each game As IHomeGame in _GamesToOpen.GetIterator()

Why, when I get a NullReferenceException, can I get the line number in the stack trace, but not the name of the object that equals null. In other words, why:

Object reference not set to an instance of an object.

instead of

_GamesToOpen is not set to an instance of an object.

or

Anonymous object returned by _GamesToOpen.GetIterator() is null.

or

game was set to null.

Is this strictly a design choice, meant to protect the anonymity of the code or is there a compelling reason in compiler design not to include this information in the debug-time exception?

回答1:

Exceptions are runtime things, variables are compile time things.

In fact, the variable in your example is an expression. Expressions are not always simple variables. At runtime, the expression will be evaluated and the method will be called on the resulting object. If the value of that expression is null, the runtime will throw a NullReferenceException. Assume the following:

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

What error message should the runtime return if the GetNullValue() method returns null?



回答2:

For languages like Java that are compiled to bytecode that gets interpreted by a VM, suppose you have a class X with a field x, and its value is null for a certain reference. If you write

x.foo()

the bytecode might look like this:

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

The point is that the operation that needs a non-null reference on the stack to operate on it, like invokeMethod in the example, cannot and does not know where that null reference came from.



回答3:

A simple way to catch this for debugging to to place an Assert statement prior to using an object, check for null and output a meaningful message.



回答4:

In release builds, the variable names are stripped out from the symbols and the code might even be optimized to not have a specific memory location for a variable, but just keep the reference in one of the registers (depending on the scope of the variable usage). Thus, it might not be possible to deduct the name of the variable from the reference location.

In debug build, there's more information available about the variables. However, the exception object needs to work the same way irregardless of the build flavor. Hence, it acts upon the minimum information it can access in any flavor.



回答5:

A couple of things...

1) when you make your own exceptions keep this in mind (if you are annoyed it it for this someone else will be annoyed at you if you do it for something else). Given that the exception path should not at all be the typical path the time spent making the exception have useful information is well worth it.

2) as a general programming practive adopt this style and you will have far less issues (yes your code will be longer in terms of lines, but you will save a lot of time):

a) never do a.b().c(); do x = a.b(); x.c(); (on separate lines) that way you can see of a was null or if the return of a.b() is null.

b) never pass the return of a method call as a parameter - always pass variables. a(foo()); should be x = foo(); a(x); This one is more for debugging and being able to see the value.

I don't know why environments like .net and Java do not provide a version of the runtime that does have more information on these sorts of exceptions, such as what the index was on an array out of bounds, the name of the variable when it is null, etc...