Подтвердить что ты не робот

Обнаружение того, чем является целевой объект, когда выбрано NullReferenceException

Я уверен, что мы все получили чудесно неопределенное исключение "Object reference not set to instance of Object" в какой-то момент. Идентификация объекта, являющегося проблемой, часто представляет собой утомительную задачу установки контрольных точек и проверки всех членов в каждом утверждении.

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

- изменить

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

@W. Craig Trader

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

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

4b9b3361

Ответ 1

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

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

[Изменить для соответствия редактированию вопроса:]

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

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

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


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

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

Ответ 2

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

Как сказать, что VS сломается, когда будут удалены необработанные исключения

  • Отладочное меню | Исключения (или Ctrl + Alt + E)
  • Сверьтесь с исключениями времени выполнения Common Language
  • Дрель в систему
  • Найдите 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. Поэтому, если вы используете отладчик, вы можете установить точку останова для этого выражения, и если вы посмотрите на окно callstack (Debug | Windows | Callstack), вы можете увидеть и перейти к строке, вызвавшей исключение.

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


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

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

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

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

Ответ 3

Не будет ли трассировка стека этой информацией?

Ответ 4

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

Ответ 5

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

Ответ 6

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

Edit @your edit:)

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

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

Хорошая точка. Для пустого значения, которое передается в метод, необходимо отправить ArgumentNullException. Для переменной-члена, которая еще не была инициализирована, что-то вроде InvalidStateException, вероятно, было бы хорошо бросить. К сожалению, я не могу найти такого исключения в MSDN. Сверните свой собственный?

Ответ 8

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

Ответ 9

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

Ответ 10

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

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

Ответ 11

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