Почему «ссылка на объект не установлена ​​для экземпляра объекта» не говорит нам, какой объект?

39

Мы запускаем систему и иногда получаем известное исключение NullReferenceExceptionс сообщением Object reference not set to an instance of an object.

Тем не менее, в методе, где у нас есть почти 20 объектов, наличие журнала, в котором говорится, что объект нулевой, на самом деле вообще бесполезен. Это все равно что сказать вам, когда вы агент безопасности на семинаре, что человек из 100 участников - террорист. Это действительно бесполезно для вас. Вы должны получить больше информации, если вы хотите определить, какой человек является угрожающим человеком.

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

Теперь, что-то одержимо в моей голове в течение нескольких месяцев, а именно:

Почему .NET не дает нам имя или, по крайней мере, тип ссылки на объект, который является нулевым? , Разве он не может понять тип из отражения или любого другого источника?

Кроме того, каковы лучшие практики, чтобы понять, какой объект является нулевым? Должны ли мы всегда проверять обнуляемость объектов в этих контекстах вручную и регистрировать результат? Есть ли способ лучше?

Обновление: исключение The system cannot find the file specifiedимеет ту же природу. Вы не можете найти какой файл, пока не присоединитесь к процессу и не отладите. Я думаю, что эти типы исключений могут стать более разумными. Разве не было бы лучше, если бы .NET мог сказать нам c:\temp.txt doesn't exist.вместо этого общего сообщения? Как разработчик, я голосую за.

Саид Нямати
источник
14
Исключение должно включать трассировку стека с номером строки. Я бы начал свое расследование с того момента, чтобы рассмотреть все объекты, к которым обращались в этой строке.
PersonalNexus
2
Кроме того, я всегда удивлялся, почему в диалоге помощника по исключениям в Visual Studio есть «полезный» совет, который можно использовать newдля создания экземпляров класса. Когда такой намек действительно помогает?
PersonalNexus
1
Если у вас есть цепочка из десяти объектных вызовов, тогда у вас есть проблемы со связью в вашем дизайне.
Пит Киркхам
4
увидеть этот вопрос stackoverflow.com/questions/14787580/...
9
Мне нравится, что каждый ответ на этот вопрос звучит так: «Используйте отладчик, регистрируйте свои ошибки, проверяйте на ноль, в любом случае, это ваша вина», которые не отвечают на вопрос, а обвиняют вас. Только на stackoverflow кто-то на самом деле дает вам ответ (который, я думаю, говорит о том, что виртуальная машина слишком большая для отслеживания). Но на самом деле, единственные люди, которые могут правильно ответить на этот вопрос, это кто-то из Microsoft, который работал над фреймворком.
Роклан

Ответы:

28

В NullReferenceExceptionосновном говорит вам: вы делаете это неправильно. Ни больше ни меньше. Наоборот, это не полноценный инструмент отладки. В этом случае я бы сказал, что вы делаете это неправильно, потому что

  • есть исключение NullReferenceException
  • Вы не предотвратили это таким образом, вы знаете, почему / где это произошло
  • а также возможно: метод, требующий 20 объектов

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

void Method(string a, SomeObject b)
{
    if (a == null) throw ArgumentNullException("a");
    if (b == null) throw ArgumentNullException("b");

    // See how nice this is, and what peace of mind this provides? As long as
    // nothing modifies a or b you can use them here and be 100% sure they're not
    // null. Should they be when entering the method, at least you know which one
    // is null.
    var c = FetchSomeObject();
    if(c == null)
    {
        throw InvalidOperationException("Fetching B failed!!");
    }

    // etc.
}

Вы также можете посмотреть на Code Contracts , у него есть свои причуды, но он работает довольно хорошо и экономит время при наборе текста.

Стейн
источник
17
@SaeedNeamati вы не подразумеваете, что, поскольку у вас большая кодовая база, вы не должны делать приличную проверку ошибок, верно? Imo чем больше проект, тем важнее становятся роли или проверка ошибок и отчетность.
Стийн
6
+1 за хороший совет, даже если он не отвечает на реальный вопрос (на который, вероятно, может ответить только Андерс Хейлсберг).
Росс Паттерсон
12
+1 за отличный совет. @ SeedNeamati вы должны прислушиваться к этому совету. Ваша проблема вызвана небрежностью и недостатком профессионализма в коде. Если у вас нет времени на написание хорошего кода, у вас гораздо большие проблемы ...
MattDavey
3
Если у вас нет времени на написание хорошего кода, то у вас точно нет времени на написание плохого кода. Написание плохого кода занимает больше времени, чем написание хорошего кода. Если вы действительно не заботитесь об ошибках. И если вас действительно не волнуют ошибки, зачем вообще писать программу?
MarkJ
5
@SaeedNeamati I only say that checking every object to get sure that it's not null, is not a good methodСерьезно. Это лучший метод. И не просто ноль, проверьте каждый аргумент на разумные значения. Чем раньше вы поймаете ошибки, тем легче найти причину. Вам не нужно возвращать несколько уровней в трассировке стека, чтобы найти вызывающий вызов.
JGauffin
19

Это действительно должно показать именно то, что вы пытаетесь назвать. Это все равно что сказать: «Есть проблема. Тебе нужно ее исправить. Я знаю, что это такое. Я не собираюсь говорить тебе. Пойди разберись». По иронии судьбы, похоже на половину ответов об этом переполнении стека.

Так насколько полезным было бы, например, если бы вы получили это ...

Object reference (HttpContext.Current) not set to instance of an object

...? Чтобы пройтись по коду, пройтись по нему и понять, что то, что вы пытаетесь назвать, nullэто хорошо, но почему бы просто не дать нам небольшую руку помощи?

Я согласен, что обычно полезно пройтись по коду, чтобы найти ответ (потому что вы, вероятно, узнаете больше), но часто бы сэкономили много времени и разочарования, если бы NullReferenceExceptionтекст был больше похож на пример выше.

Просто говорю.

LiverpoolsNumber9
источник
Как среда выполнения должна знать, что такое null?
Будет
3
Среда выполнения имеет больше информации, чем предоставляет. Какую бы полезную информацию он не имел, он должен предоставить. Это все, что я говорю. Отвечая на ваш вопрос, почему он не знает? Если вы знаете ответ на этот вопрос, вы можете предоставить лучший комментарий, чем вы.
LiverpoolsNumber9
Договорились, и я бы сказал то же самое для KeyNotFoundExceptionмногих других неприятностей ...
Синелав
На самом деле, в случае нулевой разыменования это выглядит как ограничение в способе разыменования CLR (благодаря комментарию Шахруза Джефри по вопросу ОП)
sinelaw
1
blogs.msdn.microsoft.com/dotnet/2016/08/02/… НАКОНЕЦ !!!
LiverpoolsNumber9
4

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

Конечно, это не поможет вам в этом случае:

Foo.Bar.Baz.DoSomething()

Принцип « не спрашивай» может помочь избежать такого кода.

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

Пол Стовелл
источник
2

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

Если исключение с нулевым указателем было средством отладки, оно сразу же прерывало бы выполнение программы, позволяя подключаться отладчику, направляя его прямо на инкриминирующую строку. Это даст программисту всю контекстную информацию, которая доступна. (Это в значительной степени то, что делает Segfault из-за доступа с нулевым указателем в C, хотя и немного грубо.)

Однако исключение нулевого указателя разработано как допустимое условие времени выполнения, которое может быть сгенерировано и перехвачено в нормальном потоке программы. Следовательно, соображения производительности должны быть приняты во внимание. А любая настройка сообщения об исключении требует создания, объединения и уничтожения строковых объектов во время выполнения. Таким образом, статическое сообщение, несомненно, быстрее.

Я не говорю, что среда выполнения не может быть запрограммирована таким образом, чтобы получить имя инкриминирующей ссылки. Это может быть сделано. Это сделало бы исключения даже медленнее, чем они. Если кому-то это достаточно важно, такую ​​функцию можно было бы сделать переключаемой, чтобы она не замедляла работу производственного кода, а позволяла упростить отладку; но почему-то никто, кажется, не заботился достаточно.

cmaster
источник
1

Я думаю, что Cromulent ударил гвоздь по голове, но есть и очевидный момент: если у NullReferenceExceptionвас есть неинициализированная переменная (и). Аргумент о том, что у вас есть около 20 объектов, передаваемых в метод, нельзя назвать смягчающим фактором: как создатель куска кода вы должны нести ответственность за его действия, что включает его соответствие остальной части кодовой базы, так как а также правильное и правильное использование переменных и т. д.

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

Что касается Object reference not set to an instance of an objectкода, он не может угадать значения, которые нам могут понравиться: это наша работа как программистов, и это просто означает, что вы передали неинициализированную переменную в.

GMasucci
источник
Вы понимаете, что раньше люди предлагали подобные оправдания для написания на ассемблере? И не использовать автоматическую сборку мусора? А умение делать арифметику - в гексе? «обременительный, утомительный, а иногда и скучный» - вот как мы описываем работу, которая должна быть автоматизирована.
Spike0xff
0

Научитесь пользоваться отладчиком. Это именно то, для чего она предназначена. Установите точку останова для рассматриваемого метода, и все готово.

Просто пройдитесь по своему коду и посмотрите, какие значения всех ваших переменных находятся в определенных точках.

Изменить: Честно говоря, я в шоке, что еще никто не упомянул об использовании отладчика.

Cromulent
источник
2
То есть вы говорите, что все исключения могут быть легко воспроизведены при использовании отладчика?
jgauffin
@jgauffin Я говорю, что вы можете использовать отладчик, чтобы увидеть, почему в коде реального мира генерируется исключение, а не синтетические модульные тесты, которые могут фактически не полностью тестировать рассматриваемый код, или сами модульные тесты могут иметь ошибки, из-за которых их пропустить ошибки в реальном коде. Отладчик превосходит любой другой инструмент, о котором я только могу подумать (кроме, возможно, таких вещей, как Valgrind или DTrace).
Cromulent
1
То есть вы говорите, что у нас всегда есть доступ к неисправному компьютеру и что отладка лучше, чем юнит-тесты?
jgauffin
@jgauffin Я говорю, что если у вас есть выбор, используйте отладчик в предпочтении другим инструментам. Конечно, если у вас нет такого выбора, то это немного не стартер. Очевидно, что это не так с этим вопросом, поэтому я дал ответ, который я сделал. Если бы вопрос заключался в том, как решить эту проблему на клиентском компьютере без возможности удаленной отладки (или локальной отладки), мой ответ был бы другим. Вы, кажется, пытаетесь исказить мой ответ способами, которые не имеют отношения к рассматриваемому вопросу.
Cromulent
0

Если вы хотите пойти с ответом @ stijn и поставить нулевые проверки в своем коде, этот фрагмент кода должен помочь. Вот некоторая информация о фрагментах кода . Как только вы это настроите, просто введите argnull, дважды нажмите вкладку, а затем заполните пробел.

<CodeSnippet Format="1.0.0">
  <Header>
    <Title>EnsureArgNotNull</Title>
    <Shortcut>argnull</Shortcut>
  </Header>
  <Snippet>
    <Declarations>
      <Literal>
        <ID>argument</ID>
        <ToolTip>The name of the argument that shouldn't be null</ToolTip>
        <Default>arg</Default>
      </Literal>
    </Declarations>
    <Code Language="CSharp">
      <![CDATA[if ($argument$ == null) throw new ArgumentNullException("$argument$");$end$]]>
    </Code>
  </Snippet>
</CodeSnippet>
user2023861
источник
примечание: в c # 6 теперь есть nameofтакой фрагмент throw new ArgumentNullException(nameof($argument$))кода, который имеет такие преимущества, как отсутствие магических констант, проверка компилятором и лучшая работа с инструментами рефакторинга
stijn