Определение целевого объекта при возникновении исключения NullReferenceException

Я уверен, что все мы когда-то получали удивительно расплывчатое исключение «Ссылка на объект не соответствует экземпляру объекта». Идентификация объекта, вызывающего проблему, часто является утомительной задачей, заключающейся в установке точек останова и проверке всех членов в каждом операторе.

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

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

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

@ W. Craig Trader

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

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

Я не понимаю - если вы установите прерывание отладчика на NullReferenceExceptions, вы наверняка сможете увидеть, какая переменная вызывает ошибку в точке, в которой возникло исключение?

Grokys 22.09.2008 19:39
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
31
1
14 818
10
Перейти к ответу Данный вопрос помечен как решенный

Ответы 10

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

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

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

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

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

Отредактируйте @ ваше редактирование :)

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

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

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

Вы можете подумать о InvalidOperationException, который предназначен для случаев, когда [...] a method call is invalid for the object's current state

Robert Paulson 27.05.2010 08:55

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

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

Ответ принят как подходящий

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

Лучшее решение такого рода проблем - Дизайн по контракту, либо через встроенные языковые конструкции, либо через библиотеку. 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, потому что вы только запутаете следующего программиста, который должен поддерживать ваше приложение.

+1 за «слишком много делать на одной строчке кода», поскольку я решил, что это моя проблема.

AaronLS 17.05.2012 02:20

Все, что мне нужно, это слово «foo» (имя ссылки, которая была нулевой в вашем примере) где-нибудь в объекте исключения! Почему это так сложно? Это строка прямо из кода, мне не нужен объект (который, как вы упомянули, не существует), мне просто нужна эта простая строка! Хотя DbC - отличная идея, stackoverflow.com/a/116424/1739000 на самом деле является более полезным ответом, если вы столкнулись с ошибкой.

NH. 29.03.2017 01:02

@nh Microsoft CLR не отслеживает имена символов во время выполнения (как и Java JVM). Да, в сборке есть имена символов (если вы их не удалили или не запутали), но базовый байтовый код не ссылается на таблицу символов, а только для смещения в пространстве памяти. Если вам нужно имя символа, вам нужно будет подключить к процессу отладчик, установить точку останова в соответствующей строке, а затем проверить нулевые значения для ваших переменных.

Craig Trader 14.04.2017 19:26

@CraigTrader, я думаю, вы хотели инициализировать свой объект. То, что у вас есть, дает ошибку компилировать. То, что вы пытаетесь, - это исключение время выполнения.

NH. 28.09.2018 02:00

Когда я написал этот ответ десять несколько лет назад, этот код был законным, и foo будет инициализирован нулевым значением. Итак, Microsoft наконец-то изменила компилятор, чтобы улавливать больше ошибок кода - им это хорошо. Более того, код не предназначался для компиляции или запуска, а для того, чтобы указать на проблему (неинициализированный объект) и ее влияние, не отвлекая внимание.

Craig Trader 05.10.2018 07:48

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

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

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

Как сказать VS прекращать работу при генерировании необработанных исключений

  • Меню отладки | Исключения (или Ctrl + Alt + E)
  • Детализация исключений среды CLR
  • Разберитесь в системе
  • Найдите 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), вы могли бы получить большую часть информации, необходимой для отслеживания ошибка из Exception ToString с включенной трассировкой стека с номером строки.

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

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

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

Каждый день я узнаю что-то новое о том, насколько мощным является инструмент отладки в VS. Большое спасибо.

Didier A. 21.06.2012 18:24

Наконец, через неделю разработки приложений для Windows и поиска более чем исключения без строки и класса. Это сломается, куда бросит! Как это по умолчанию снято!

Laurent Russier 13.03.2015 17:49

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

Другие вопросы по теме