Ссылка на объект не установлена ​​на экземпляр объекта. Почему .NET не показывает, какой объект имеет значение null?


103

Относительно этого сообщения о необработанном исключении .NET:

В экземпляре объекта не задана ссылка на объект.

Почему .NET не показывает, какой это объект null?

Я знаю, что могу проверить nullи устранить ошибку. Однако почему .NET не помогает указать, какой объект имеет нулевую ссылку и какое выражение вызвало NullReferenceException?

c#  .net 

2
Когда это произойдет, перепишите строку, в которой это произошло, чтобы она сначала проверяла каждый возможный результат на ноль - тогда вы точно будете знать, что это было. Либо так, либо подключите замечательный отладчик Visual Studio, который прерывает момент возникновения исключения и позволяет увидеть, что имеет значение null :)
— Паташу,

5
Не совсем, он просто спрашивает, почему .NET framework не помогает программисту показать, какой объект имеет значение NULL. Я думаю, это снижение производительности (вам нужно подумать). но я тоже не уверен.
— bas

1
@bas: Хотя это правда, вопрос немного вводит в заблуждение, поскольку он должен спрашивать о «части выражения», а не о «объекте». Это также объясняет, почему простое отражение не поможет, но потребуется некоторая обширная отладочная информация.
— OR Mapper

4
Тем не менее мне любопытен ответ. Это похоже на исключения .net, которые не помогают указать, какой ключ не существует в словаре. Кроме того, я не понимаю преданных по этому вопросу.
— bas

12
Терминология, пожалуйста: объект никогда не бывает нулевым. Ссылка на объект может быть , хотя. Но ссылка на объект - это просто место в памяти - как это может вам помочь, если у вас все равно не подключен отладчик?
— Оскар Берггрен

Ответы:


169

(Информацию о новом помощнике по исключениям в Visual Studio 2017 см. В конце этого ответа)


Рассмотрим этот код:

String s = null;
Console.WriteLine(s.Length);

Это вызовет появление NullReferenceExceptionво второй строке, и вы хотите знать, почему .NET не сообщает вам, что это sбыло null, когда было создано исключение.

Чтобы понять, почему вы не получаете эту информацию, вы должны помнить, что выполняется не исходный код C #, а скорее IL:

IL_0001: ldnull      
IL_0002: stloc.0 // с
IL_0003: ldloc.0 // с
IL_0004: callvirt System.String.get_Length
IL_0009: вызов System.Console.WriteLine

Это callvirtкод операции, который генерирует NullReferenceExceptionи делает это, когда первым аргументом в стеке оценки является пустая ссылка (та, которая была загружена с использованием ldloc.0).

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


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

public void Foo(String s) {
  if (s == null)
    throw new ArgumentNullException("s");
  Console.WriteLine(s.Length);
}

Если методу передается null, вы получаете исключение, которое точно описывает, в чем проблема (то sесть null).


Четыре года спустя в Visual Studio 2017 появился новый помощник по исключениям, который попытается определить, что имеет значение null при NullReferenceExceptionвызове. Он даже может предоставить вам необходимую информацию, когда это возвращаемое значение метода имеет значение NULL:

Помощник по исключениям Visual Studio 2017

Обратите внимание, что это работает только в сборке DEBUG.


5
Номера строк и имена исходных файлов также не хранятся в самом коде IL, или они? Тем не менее, их можно сделать доступными для отладки.
— OR Mapper

4
@MartinLiversage: Совершенно верно. Итак, вопрос сводится к следующему: почему в файлах символов хранится недостаточно информации, которая также сообщает, какое выражение в коде было оценено null.
— OR Mapper

2
@MartinLiversage: файлы символов доступны таким образом, что при возникновении исключения, наряду с сообщением об исключении, исходный файл и номер строки могут отображаться в выводе отладчика. Итак, вопрос в том, в чем причина отсутствия дополнительной информации о том, что именно было возвращено null- обратите внимание, что OP не утверждает, что он или она хочет знать, что для сборок выпуска также может быть достаточно сборок отладки.
— OR Mapper

3
Хм, и мы не должны забывать, что на самом деле выполняется даже не IL, а собственный код, созданный из него во время выполнения.
— Оскар Берггрен

2
@MartinLiversage: Никто в этом вопросе не утверждает, что мы хотим, чтобы это полностью поддерживалось для сборок выпуска. В любом случае, я не совсем вижу проблему в корреляции кода операции IL, который использует ссылку на объект (которая может оказаться таковой null), со строкой и столбцом исходного файла, которые вернули эту ссылку на объект.
— OR Mapper

9

Как вы хотите, чтобы сообщение об ошибке выглядело в следующем случае?

AnyObject.GetANullObject().ToString();

private object GetANullObject()
{
  return null;
}

Здесь нет имен переменных!


2
Я подозреваю, что OP ищет в исходном коде выражение, которое возвращает null, а не объект. Я добавил соответствующий комментарий к вопросу и надеюсь, что он или она прояснят ситуацию. Если мои подозрения верны, OP будет ожидать что-то вроде Object reference obtained from AnyObject.GetANullObject() not set to an instance of an object.сообщения об ошибке.
— OR Mapper

1
@ORMapper Согласен. Я бы просто поставил свой «ответ» в комментарии к OP, если бы у меня было достаточно очков репутации, чтобы добавить комментарий!
— romar

1
«пустая ссылка пыталась вызвать метод ToString () класса XYZ» была бы более полезной, чем то, что мы получили сейчас.
— Майкл Леви,

Самым полезным будет трассировка стека, показывающая на каждом уровне вызова, какая именно строка и в каком файле привела к ошибке. Ой, подожди ... вот что он делает сейчас!
— Джим Балтер

1

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

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

but why .NET don't tell us which object is null? Потому что он не знает, какой объект нулевой. Объекта просто не существует!

То же самое, когда я говорю, что C # скомпилирован в код .NET IL. Код .NET IL не знает имен или выражений. Он знает только ссылки и их местонахождение. Здесь тоже нельзя получить то, чего не существует. Выражение или имя переменной не существует.

Философия: вы не сможете приготовить омлет, если у вас вообще нет яйца.


3
это тоже не ответ :)
— bas

Как получить ссылку, если ее не существует? @Bas
— Аникет Инге

4
«Что ж, это задача инженеров Microsoft ответить». Так что пусть они проливают свет на это вместо того, чтобы констатировать очевидное
— bas

@bas мы можем решить, что, по крайней мере, логично. По логике объекта не существует. Как вы его поймаете с исключением, а потом распечатаете имя объекта? Его просто не существует. Даже в стеке ..
— Аникет Инге

так что ответ таков: практически невозможно указать, какой объект имеет нулевую ссылку? Это тоже ответ. Я не утверждаю, что знаю, мне просто нравится вопрос :). +1 за все усилия: p
— bas

1

Не уверен, но это может быть потому, что .Net не знает, является ли он предопределенным классом или определяемым пользователем. Если он предопределен, он может быть нулевым (например, строка, занимающая 2 байта), но если она определяется пользователем, мы должны создать его экземпляр, чтобы он знал, что этот объект будет занимать столько памяти. Поэтому он выдает ошибку во время выполнения.


-2

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

Ваш лучший вариант - запустить его в отладчике с отладочной информацией, и ваша IDE сломается на ошибочной строке (довольно четко демонстрируя, что полезная информация действительно доступна).

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.