Почему исключение с нулевой ссылкой не может назвать объект, имеющий нулевую ссылку?

Мне кажется, что много времени на отладку уходит на поиск исключений с нулевой ссылкой в ​​сложных операторах. Например:

For Each game As IHomeGame in _GamesToOpen.GetIterator()

Почему, когда я получаю исключение NullReferenceException, могу ли я получить номер строки в трассировке стека, но не имя объекта, равного нулю. Другими словами, почему:

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.

Является ли это чисто дизайнерским выбором, предназначенным для защиты анонимности кода, или есть веская причина в дизайне компилятора, чтобы не включать эту информацию в исключение во время отладки?

Ответов (5)

Решение

Исключения - это вещи времени выполнения, переменные - вещи времени компиляции.

Фактически, переменная в вашем примере является выражением . Выражения не всегда являются простыми переменными. Во время выполнения выражение будет оценено, и метод будет вызван для полученного объекта. Если значение этого выражения равно null, среда выполнения выдаст файл NullReferenceException . Предположим следующее:

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

Какое сообщение об ошибке должна вернуть среда выполнения, если GetNullValue() метод возвращается null?

В сборках выпуска имена переменных удаляются из символов, и код может быть даже оптимизирован, чтобы не было определенного места в памяти для переменной, а просто сохранить ссылку в одном из регистров (в зависимости от области использования переменной. ). Таким образом, может оказаться невозможным вычесть имя переменной из ссылочного местоположения.

В отладочной сборке доступна дополнительная информация о переменных. Однако объект исключения должен работать одинаково независимо от типа сборки. Следовательно, он действует на основе минимального количества информации, к которой он может получить доступ в любом варианте.

Простой способ поймать это для отладки, чтобы разместить оператор Assert перед использованием объекта, проверить наличие null и вывести содержательное сообщение.

Пару вещей ...

1) когда вы делаете свои собственные исключения, имейте это в виду (если вас это раздражает, то кто-то другой будет раздражаться на вас, если вы сделаете это для чего-то другого). Учитывая, что путь исключения вообще не должен быть типичным путем, время, потраченное на создание исключения, имеет полезную информацию, оно того стоит.

2) в качестве общей практики программирования используйте этот стиль, и у вас будет гораздо меньше проблем (да, ваш код будет длиннее с точки зрения строк, но вы сэкономите много времени):

а) никогда не выполняйте ab (). c (); сделать x = ab (); xc (); (в отдельных строках) таким образом вы можете увидеть, что a было null или если ab () возвращено null.

б) никогда не передавайте возврат вызова метода в качестве параметра - всегда передавайте переменные. а (foo ()); должно быть x = foo (); а (х); Это больше для отладки и возможности увидеть значение.

Я не знаю, почему такие среды, как .net и Java, не предоставляют версию среды выполнения, в которой есть дополнительная информация об этих видах исключений, например о том, какой индекс был в массиве за пределами границ, имя переменной, когда это ноль и т. д.

Для таких языков, как 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 в примере, не может и не знает, откуда взялась эта нулевая ссылка.