Обнаружение того, что является целевым объектом, когда выбрасывается NullReferenceException - PullRequest
29 голосов
/ 22 сентября 2008

Я уверен, что все мы когда-то получили удивительно смутное исключение "Ссылка на объект не установлена ​​на экземпляр объекта". Определение объекта, который является проблемой, часто является утомительной задачей установки точек останова и проверки всех членов в каждом утверждении.

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

- редактирование

Кажется, я был расплывчатым, как исключение =). Смысл в том, чтобы _не нужно было отлаживать приложение, чтобы найти ошибочный объект. Компилятор / среда выполнения знают, что объект был выделен / объявлен, и что объект еще не был создан. Есть ли способ извлечь / идентифицировать эти детали в пойманном исключении

@ W. Craig Trader

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

Спасибо за все предложения.

Ответы [ 10 ]

17 голосов
/ 22 сентября 2008

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

Лучшим решением проблемы такого рода является Проектирование по контракту , либо через встроенные языковые конструкции, либо через библиотеку. DbC предложил бы предварительно проверить любые входящие аргументы для метода для данных вне диапазона (то есть: Null) и выдавать исключения, потому что метод не будет работать с неверными данными.

[Изменить, чтобы сопоставить вопрос. Edit:]

Я думаю, что описание NRE вводит вас в заблуждение. Проблема, с которой сталкивается CLR, заключается в том, что его попросили разыменовать ссылку на объект, когда ссылка на объект является нулевой. Возьмите этот пример программы:

public class NullPointerExample {
  public static void Main()
  {
    Object foo;
    System.Console.WriteLine( foo.ToString() );
  }
}

Когда вы запустите это, он сгенерирует NRE в строке 5, когда попытается оценить метод ToString () для foo. Нет объектов для отладки, только ссылка на неинициализированный объект (foo). Есть класс и метод, но нет объекта.


Re: Крис Марасти-Георг ответ :

Вы никогда не должны бросать NRE самостоятельно - это системное исключение со специальным значением: CLR (или JVM) попыталась оценить ссылку на объект, которая не была инициализирована. Если вы предварительно проверите ссылку на объект, то либо сгенерируете какое-то недопустимое исключение аргумента, либо исключение для конкретного приложения, но не NRE, потому что вы только запутаете следующего программиста, который должен поддерживать ваше приложение.

17 голосов
/ 22 сентября 2008

Как уже указывалось в нескольких ответах, скажите Visual Studio, что нужно использовать Throw для NullReferenceException.

Как сказать VS, чтобы он ломался при возникновении необработанных исключений

  • Меню отладки | Исключения (или Ctrl + Alt + E )
  • Детализация общеязыковых исключений времени выполнения
  • Детализация в системе
  • Найдите System.NullRefernceException и установите флажок Разрывать всякий раз, когда выдается это исключение, вместо того, чтобы позволять ему переходить к любым установленным блокам Catch

Так что теперь, когда это произойдет, VS немедленно прервется, и строка Current Statement будет сидеть в выражении, которое имеет значение null.

Эта возможность полезна для всех видов исключений, включая пользовательские (можно добавить полное имя типа, и VS будет соответствовать ему во время отладки)

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


Еще одна хитрость, которая может пригодиться (но только в некоторых языках) - это использование ключевого слова When (или эквивалентного) ... В VB это выглядит как

Try
  ' // Do some work           '
Catch ex As Exception When CallMethodToInspectException(ex)

End Try

Хитрость в том, что выражение When оценивается до того, как стек вызовов разматывается на блок Catch . Поэтому, если вы используете отладчик, вы можете установить точку останова в этом выражении, и если вы посмотрите на окно стека вызовов (Debug | Windows | Callstack), вы сможете увидеть и перейти к строке, которая вызвала исключение.

(Вы можете выбрать возврат false из CallMethodToInspectException, поэтому блок Catch будет игнорироваться, и среда выполнения продолжит поиск в стеке соответствующего блока Catch - что может позволить вести запись, которая не влияет на поведение, и с меньшими накладными расходами, чем улов и повторный бросок)


Если вы просто заинтересованы в неинтерактивном ведении журнала, то, если у вас есть отладочная сборка (или, в некоторой степени, поскольку у вас есть проблемы с оптимизацией, сборка выпуска с PDB), вы можете получить большую часть необходимой информации. чтобы отследить ошибку из исключительной ситуации ToString, с включенным номером трассировки стека с номером строки.

Если, однако, номера строки было недостаточно, вы также можете получить номер столбца (в значительной степени, конкретное локальное выражение или выражение, равное нулю), извлекая StackTrace для исключения (используя либо вышеприведенную технику, либо просто в сам блок захвата):

int colNumber = new System.Diagnostics.StackTrace(ex, true).GetFrame(0).GetFileColumnNumber();

Хотя я не видел, что он делает для NullReference или других сгенерированных во время выполнения исключений, он также может быть заинтересован в рассмотрении Exception Hunter как инструмента статического анализа.

1 голос
/ 22 сентября 2008

Строка # и файл - обычно все, что вам нужно, чтобы найти преступника. Если вы выбрасываете исключение, рассмотрите возможность использования ArgumentNullException, если это уместно, или проверку на нулевые значения и добавьте NullReferenceException s, которые содержат более подробную информацию о пустом поле.

Edit @ your edit:)

AFAIK, вам нужно изучить строку трассировки стека, чтобы получить эту строку # и файл. Лучше всего было бы получить самое внутреннее исключение, а затем взглянуть на первую строку трассировки стека. Если вы хотите иметь возможность программно анализировать эту информацию, чтобы выяснить, какое поле вызвало нулевое значение, и что-то сделать с именем этого поля, я боюсь, что вам не повезет.

@ W. Крейг Трейдер

Хороший вопрос. Для нулевого значения, которое передается в метод, должно быть выброшено <a href="http://msdn.microsoft.com/en-us/library/system.argumentnullexception.aspx" rel="nofollow noreferrer">ArgumentNullException</a>. Для переменной-члена, которая еще не была инициализирована, было бы неплохо что-то вроде InvalidStateException. К сожалению, я не могу найти такого исключения в MSDN. Бросай свой?

1 голос
/ 22 сентября 2008

Ну, вы не можете реально идентифицировать объект, так как он не существует и, следовательно, вы получаете исключение.

1 голос
/ 22 сентября 2008

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

0 голосов
/ 25 ноября 2008

Что касается настройки Visual Studio для перехвата исключения (как предлагается здесь ), НЕ ЗАБУДЬТЕ удалить эту опцию, как только вы исправите проблему. Я просто потратил полчаса, пытаясь понять, почему мое приложение зависло в какой-то части System.Windows.Forms ....

0 голосов
/ 22 сентября 2008

Для справки, похожая тема: Должен ли я перехватывать исключения только для их регистрации?

Существенным моментом является то, что вы хотите эффективно захватить исключение. По моему опыту, цель состоит в том, чтобы убедиться, что программист проверяет наличие нулевых ссылок в коде - однако мы знаем, что в действительности мы пропускаем некоторые из них. Код интерфейса должен иметь некоторый уровень обработки исключений. Мне понравился мой ответ на этот вопрос: Мой ответ . Что еще более важно, комментарий 1800 information , который указал, что вы просто выбрасываете, а не выбрасываете ex, чтобы захватить всю трассировку стека, как в конечном итоге вы устраняете эти проблемы.

0 голосов
/ 22 сентября 2008

Установите VS для прерывания на исключениях, тогда, когда вы получаете свою ошибку, обычно довольно очевидно, в какой строке она находится. Окно трассировки стека расскажет вам, как вы туда попали. Больше ничего вы не можете сделать, кроме этого.

0 голосов
/ 22 сентября 2008

Если вы перехватываете исключения для дружественных пользовательских сообщений или регистрации, вы, вероятно, захотите, чтобы отладчик остановился на исключении во время отладки. Перейдите в раздел «Отладка / Исключения» и проверьте типы исключений, на которых вы хотите, чтобы отладчик прекращал работать, System.NullReferenceException в вашем случае.

0 голосов
/ 22 сентября 2008

вы можете проверить свойства Message и InnerException

http://msdn.microsoft.com/en-us/library/system.exception.innerexception.aspx

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...