Посилання на об'єкт не встановлено для примірника об'єкта. Чому .NET не показує, який об’єкт є "null"?


103

Щодо цього некерованого повідомлення про виключення .NET:

Посилання на об'єкт не встановлено для примірника об'єкта.

Чому .NET не показує, що є об'єктом null?

Я знаю, що можу перевірити nullі усунути помилку. Однак чому .NET не допомагає вказувати, який об’єкт має нульовий посилання та який вираз викликав NullReferenceException?

c#  .net 

2
Коли це станеться, перепишіть рядок, на якому він трапився, щоб він спочатку перевіряв кожен можливий результат на нуль - тоді ви точно будете знати, що це було. Або це, або
додайте

5
Насправді він просто запитує, чому .NET Framework не допомагає програмісту показати, який об’єкт є нульовим. Я думаю, це покарання за виконання (вам знадобиться роздум). але я також не впевнений.
bas

1
@bas: Хоча це правда, питання дещо вводить в оману, оскільки його слід задавати про "частину виразу", а не про "об'єкт". Це також пояснює, чому саме рефлексія не допоможе, але знадобиться деяка обширна інформація про налагодження.
АБО Mapper

4
Ще мені цікаво відповісти. Це схоже на винятки .net, що не допомагає вказати, який ключ не існує в словнику. Крім того, я не розумію присвячених у цьому питанні.
бас

12
Термінологія, будь ласка: Об'єкт ніколи не буває нульовим. Посилання на об'єкт може бути , хоча. Але посилання на об'єкт - це лише місце в пам'яті - як це допоможе вам, якщо ви все одно не встановите налагоджувача?
Оскар Берггрен

Відповіді:


169

(Інформацію про нового помічника винятків у Visual Studio 2017 див. У кінці цієї відповіді)


Розглянемо цей код:

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

Це викине a NullReferenceExceptionу другому рядку, і ви хочете знати, чому .NET не каже вам, що це sбуло недійсно, коли викид був викинутий.

Щоб зрозуміти, чому ви не отримуєте цю інформацію, слід пам’ятати, що не джерело C # виконує, а IL:

IL_0001: ldnull      
IL_0002: stloc.0 // s
IL_0003: ldloc.0 // s
IL_0004: callvirt System.String.get_Length
IL_0009: виклик System.Console.WriteLine

Саме callvirtопкод викидає NullReferenceExceptionі робить це, коли перший аргумент на стеці оцінювання є нульовою посиланням (той, який було завантажено за допомогою ldloc.0).

Якщо .NET повинен бути в змозі сказати, що це була sнульова посилання, то якимось чином слід було б відслідковувати, що перший аргумент на стеці оцінювання виник у формі s. У цьому випадку нам легко побачити, що це sбуло недійсно, але що робити, якщо це значення було зворотним значенням з іншого виклику функції і не зберігалося в жодній змінній? У будь-якому випадку, така інформація не є тим, що ви хочете відслідковувати у віртуальній машині, як віртуальна машина .NET.


Щоб уникнути цієї проблеми, я пропоную вам виконати перевірку нульових аргументів у всіх загальнодоступних викликах методу (якщо, звичайно, ви не дозволяєте нульової посилання):

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

Якщо null передається методу, ви отримуєте виняток, який точно описує, у чому полягає проблема (тобто snull).


Через чотири роки у Visual Studio 2017 тепер є новий помічник винятку, який спробує сказати, що є нульовим при киданні a NullReferenceException. Він навіть може надати вам необхідну інформацію, коли значення, що повертається, є нульовим:

Помічник винятку Visual Studio 2017

Зауважте, що це працює лише у складі DEBUG.


5
Номери рядків та назви вихідних файлів також не зберігаються в самому коді IL, або вони? Однак вони можуть бути доступні для налагодження.
АБО Mapper

4
@MartinLiversage: саме так. Тож питання зводиться до: Чому у файлах символів не зберігається достатня інформація, яка також говорить про те, на яке вираження в коді було оцінено null.
АБО Mapper

2
@MartinLiversage: Файли символів доступні таким чином, що за винятком, поряд із повідомленням про виняток, вихідний файл та номер рядка можуть відображатися у висновку налагоджувача. Отже, питання полягає в тому, яка причина не включати додаткову інформацію про те, що саме повернулося null- зауважте, що ОП не стверджує, що він чи вона хоче знати, що для версій версій також може бути достатньо збірок налагодження.
АБО Mapper

3
Хм, і ми не повинні забувати, що насправді виконує навіть не ІЛ, а нативний код, побудований з нього під час виконання.
Оскар Берггрен

2
@MartinLiversage: Ніхто в цьому питанні не стверджує, що ми хочемо, щоб це чудово підтримувалося для версій версій. У будь-якому випадку, я не дуже бачу проблему у співвіднесенні коду IL, який використовує посилання на об'єкт (який може виявитись null) з рядком та стовпцем вихідного файлу, який повернув цю посилання на об'єкт.
АБО Mapper

9

Як ви хочете виглядати повідомлення про помилку в наступному випадку?

AnyObject.GetANullObject().ToString();

private object GetANullObject()
{
  return null;
}

Тут немає імен змінних, щоб повідомляти!


2
Я підозрюю, що ОП шукає вираз у вихідному коді, який повертає нуль, а не об’єкт. Я додав відповідний коментар до питання і сподіваюся, що він чи вона прояснить справи. Якщо моя підозра є правильною, ОП очікує щось подібне Object reference obtained from AnyObject.GetANullObject() not set to an instance of an object.як повідомлення про помилку.
АБО Mapper

1
@ORMapper Я згоден. Я б просто поставив свою "відповідь" у коментарі до ОП, якби у мене було достатньо балів репутації, щоб додати коментар!
romar

1
"нульова посилання, яка намагалася викликати метод ToString () класу XYZ", була б кориснішою, ніж те, що ми отримуємо зараз.
Майкл Леві

Найбільш корисним було б стеження стека, що показує на кожному рівні виклику саме те, який рядок, у якому файлі призвів до помилки. О, зачекайте ... ось що це робить зараз!
Джим Балтер

1

Ну, це інженери Майкрософт, щоб відповісти. Але ви, очевидно, можете скористатися налагоджувачем і додати годинник, щоб дізнатися, хто з них має проблеми.

Однак винятком є ​​те, NullReferenceExceptionщо означає, що посилання не існує . Ви не можете отримати об'єкт, який взагалі не був створений.

but why .NET don't tell us which object is null? Тому що він не знає, який об’єкт є нульовим. Об'єкта просто не існує!

Той самий випадок, коли я кажу, що C # компілюється в .NET IL код. Код .NET IL не знає імен та виразів. Він знає лише посилання та їх місцезнаходження. І тут ви не можете отримати те, що не існує. Вираз або ім’я змінної не існує.

Філософія: Ви не можете зробити омлет, якщо у вас в першу чергу немає яйця.


3
це теж не відповідь :)
bas

Як отримати довідку, якщо вона не існує? @Bas
Inge

4
"Ну, це відповіді інженерам компанії Microsoft." Тож нехай вони прояснять це, замість того, щоб заявляти очевидне
бас

@bas ми можемо вирішити, що є принаймні логічним. Логічно об'єкта не існує. Як ви виберете його за винятком, а потім роздрукуєте назву об’єкта? Його просто не існує. Навіть не на стеці ..
Aniket Inge

тож відповідь полягає в тому, що практично неможливо вказати, який об’єкт має нульову посилання? Це теж відповідь. Я не заявляю, що знаю, мені просто подобається питання :). +1 за всі зусилля: p
bas

1

Не впевнений, але це може бути, тому що .Net не знає, чи це визначений клас або визначений користувачем. Якщо він визначений заздалегідь, він може бути нульовим (як рядок, який займає 2 байти), але якщо він визначений користувачем, ми повинні створити його примірник, щоб він знав, що цей об'єкт займе стільки пам’яті. Так що він кидає помилку під час виконання.


-2

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

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.