Может ли Visual Studio сказать, какая ссылка вызвала исключение NullReferenceException? - PullRequest
10 голосов
/ 07 декабря 2011

Я пишу модульные тесты для веб-приложения MVC, и я получаю исключения с нулевыми ссылками, потому что макетированные тестовые объекты инициализируются только частично.Я знаю, какая строка вызывает исключения, и это выглядит примерно так:

return Supervisor.RegistrationInformation.Registrations
    .Any(r =>
        r.RegistrationCountry.IsUSAOrCandada() &&
        (!DatesWorked.Start.HasValue || r.RegistrationDate <= DatesWorked.Start.Value) &&
        (!DatesWorked.End.HasValue || r.RegistrationExpirationDate >= DatesWorked.End.Value) &&
        //...

Там много ссылок, и любая из них может быть проблемой.Тем не менее, само по себе NullReferenceException, похоже, не фиксирует, какая ссылка взорвалась.Тот факт, что я передаю лямбду, представляет еще одну проблему: насколько я могу судить, я не могу пройти через лямбду во время отладки и посмотреть, какие члены r равны нулю.

Есть лиВ любом случае я могу сделать одно или оба из следующих действий:

  • Пусть Visual Studio точно скажет, какая ссылка вызвала NullReferenceException?
  • Если это не удалось, есть ли способ сделатьотладчик шагает по лямбда-выражению (или просто наводит курсор на вещи, чтобы увидеть их значения), поскольку он оценивается Any?

Я чувствую, что должен быть способ сделать эти вещи, ноЯ не могу найти это.Я использую VS2010 Premium, и у меня установлены Resharper, VS Power Tools и несколько других расширений.Если есть надстройка, которая делает это, я бы с этим согласился.

Редактировать:

Как указывает Эрик Липперт, невозможно точно определить источник исключения NR, когдакод был скомпилирован в конфигурации выпуска.Я спрашиваю только о работе в режиме отладки.Если Visual Studio (или какое-либо расширение VS) может отследить источник ссылки во время отладки, это ответило бы на мой вопрос.

Edit 2:

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

Ответы [ 8 ]

17 голосов
/ 07 декабря 2011

В общем, нет способа сделать то, что вы хотите, нет.Чтобы понять почему, подумайте о том, что происходит, когда выдается исключение нулевой ссылки.Представьте, что вы - компилятор, и вы должны создать код для обработки вызова abc.Def.Ghi.Jkl (), где abc - это локальное поле, Def и Ghi - это поля ссылочного типа, а Jkl - это метод.Нет инструкции по IL, которая может сделать что-то такое сложное;Вы должны сломать это.Таким образом, вы генерируете код для эквивалентной программы, где все намного проще.Вы отправляете фрагмент программы:

temp1 = abc.Def;
temp2 = temp1.Ghi;
temp2.Jkl();

Предположим, что temp2 равен нулю, потому что Ghi был равен нулю.Этот факт не будет раскрыт до тех пор, пока не будет вызван Jkl, и в этот момент объект, вызывающий исключение, не будет знать, как инициализируется temp2. ​​ Это произошло давно, наносекунда в прошлом и машинный коднет памяти о прошлом;нулевая ссылка не содержит в себе небольшую заметку, в которой говорится, откуда пришло нулевое значение, больше, чем когда вы говорите «a = b + c», результирующее число двенадцать не сохраняет вместе с ним записку «Я былсумма b и c ".

3 голосов
/ 08 декабря 2011

Это не решит всей вашей проблемы, но должно помочь:

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

2 голосов
/ 07 декабря 2011

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

1 голос
/ 07 декабря 2011

Непосредственная проблема заключается в том, что лямбда-оболочка объединяет много сложной логики в одном выражении, поэтому вы не можете найти, где произошел сбой.

Но это всего лишь побочный эффект. Проблема real в том, что ваш код предполагает, неправильно , что ни одна из ссылок не будет нулевой.

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

Вам нужно положить свой отладчик и подумать о коде. Весь код за один проход. Проверь его: проверь каждую часть выражения и спроси себя: «Может ли этот бит быть нулевым? Что произойдет, если он есть? И если да, то как я могу сделать его безопасным?»

Таким образом, вы сможете переписать все выражение в форме, которая, как вам известно, не имеет смысла, и вам никогда не понадобится отлаживать ее, чтобы понять, почему она взорвалась.

Например, это:

    r.RegistrationCountry.IsUSAOrCandada() && 

... может вызвать нулевую разыменование, если r == null или если r.RegistrationCountry == null. Код должен проверить эти возможности. «Самый защищенный» код должен проверять каждую ссылку примерно так:

    r != null && r.RegistrationCountry != null && r.RegistrationCountry.IsUSAOrCandada() && 

, который гарантирует, что каждый шаг будет выполнен только в случае успешного выполнения предыдущего шага. Обратите внимание, что в списке может никогда не быть r == null, поэтому проверка может не потребоваться. Или r.RegistrationCountry может быть структурой (необнуляемый тип), так что вы будете знать, что проверка не требуется. Таким образом, вы можете избежать ненужных проверок, думая об этом. Но вам нужно продумать каждую часть кода, чтобы оспорить и устранить все предположения.

1 голос
/ 07 декабря 2011

Обычно я не отвечаю без ответа, но я думаю, что ответ о том, что нет общего способа сделать это, неверен.

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

static Expression<Func<T, bool>> WithNullDebugging(Expression<Func<T, bool>> exp)
{
    for each node in the expression tree
        if node is a field, property, or method accessor
            generate a null check for this member and an exception throw
            substitute the checked node for this node
        else if the node has subexpression children
            call this method recursively on each child
            substitute each checked subexpression for the subexpression

    return the fixed expression tree
}

Я обычно не делаю метапрограммирование в C #, поэтому я не уверен, но я думаю, что это вполне возможно; кто-нибудь умный, дайте мне знать, если это не так, и я удалю или исправлю этот ответ.

1 голос
/ 07 декабря 2011

Вы можете поместить точку останова в лямбда-выражение, а когда оно попадет, вы сможете навести курсор на выражение и увидеть его значения просто отлично.

Глядя на ваш код, я вижу, что только одно из трех выражений могло вызвать NullRef- r, r.RegistrationCountry и DatesWorked.

Поместите эти три выражения в окно Watch и попросите отладчик прервать любое исключение NullReferenceException (через Debug-> Exceptions), или поместите точку останова в лямбда-выражение и сделайте ее условной точкой останова при условии r == null || r.RegistrationCountry == null || DatesWorked == null и ответ должен появиться довольно быстро.

1 голос
/ 07 декабря 2011

Если вы установите здесь точку останова, вы сможете проверить каждое из значений до того, как будет выполнена строка и будет сгенерировано исключение. Вы просто должны методично смотреть на каждый разыменованный элемент, пока не найдете Null. Visual Studio очень хорош в подобных вещах.

0 голосов
/ 29 апреля 2018

да. Вам нужно установить расширение Resharper в visual studio. Resharper - инструмент для непрерывного анализа качества кода.

Вы можете найти более подробную информацию по ссылке ниже

https://www.jetbrains.com/resharper/

...