Почему не может исключить исключение с помощью null-reference объекта, который имеет нулевую ссылку?
Мне кажется, что большая часть моего времени отладки затрачена на преследование исключений нулевой ссылки в сложных операторах. Например:
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.
Является ли это строго выбором дизайна, предназначенным для защиты анонимности кода или есть ли веская причина в дизайне компилятора не включать эту информацию в исключение времени отладки?
Ответы
Ответ 1
Исключения - это элементы времени выполнения, переменные - это время компиляции.
Фактически, переменная в вашем примере является выражением. Выражения не всегда являются простыми переменными. Во время выполнения выражение будет оцениваться, и метод будет вызван на результирующий объект. Если значение этого выражения равно null
, время выполнения вызовет NullReferenceException
. Предположим следующее:
Dim a as New MyObject
Dim b as String = MyObject.GetNullValue().ToString()
Какое сообщение об ошибке должно возвращаться во время выполнения, если метод GetNullValue()
возвращает null
?
Ответ 2
Для таких языков, как 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 в примере, не может и не знает, откуда эта нулевая ссылка.
Ответ 3
Простой способ поймать это для отладки, чтобы разместить инструкцию Assert перед использованием
объект, проверить значение null и вывести содержательное сообщение.
Ответ 4
В версиях релизов имена переменных удаляются из символов, а код может даже оптимизироваться, чтобы не иметь определенного места памяти для переменной, но просто сохраняйте ссылку в одном из регистров (в зависимости от объема использование переменной). Таким образом, было бы невозможно вычесть имя переменной из ссылочного местоположения.
В сборке отладки имеется больше информации о переменных. Однако объект исключения должен работать одинаково независимо от стиля сборки. Следовательно, он действует на минимальную информацию, которую он может получить в любом вкусе.
Ответ 5
Несколько вещей...
1), когда вы делаете свои собственные исключения, имейте это в виду (если вас это раздражает, то для этого кто-то будет вас раздражать, если вы сделаете это для чего-то другого). Учитывая, что путь исключения не должен быть типичным путем, время, затрачиваемое на создание исключения, имеет полезную информацию, это того стоит.
2), так как общее программирование использует этот стиль, и у вас будет гораздо меньше проблем (да, ваш код будет длиннее с точки зрения строк, но вы сэкономите много времени):
a) никогда не делайте a.b(). c(); do x = a.b(); x.c(); (в отдельных строках), так что вы можете видеть, что значение null было нулевым, или если возврат a.b() равен null.
b) никогда не передавать возврат вызова метода в качестве параметра - всегда передавать переменные. а (Foo()); должно быть x = foo(); а (х); Это больше для отладки и возможности видеть значение.
Я не знаю, почему такие среды, как .net и Java, не предоставляют версию среды выполнения, которая имеет больше информации об этих видах исключений, например, что индекс был в массиве за пределами границ, имя переменная, когда она равна нулю и т.д.