Чому "посилання на об'єкт не встановлено на екземпляр об'єкта" не каже нам, який об'єкт?


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 хтось насправді дає тобі відповідь (на яку я думаю, що це занадто великі накладні витрати, щоб VM відстежував). Але насправді, єдині люди, які можуть правильно відповісти на це питання - це хтось із microsoft, який працював на рамках.
Rocklan

Відповіді:


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.
}

Ви також можете ознайомитись із кодовими контрактами , у неї є химерність, але вона працює досить добре і заощаджує набір тексту.


17
@SaeedNeamati ви не маєте на увазі, що оскільки у вас є велика база коду, ви не повинні робити гідну перевірку помилок, правда? Якщо проект більший, тим важливіші ролі чи перевірка помилок та звітування про них.
Стін

6
+1 за гарну пораду, навіть якщо вона не відповідає на власне питання (напевне, може відповісти лише Андерс Хейльсберг).
Росс Паттерсон

12
+1 за відмінні поради. @SaeedNeamati, ви повинні послухати цю пораду. Ваша проблема викликана необережністю та непрофесіоналізмом коду. Якщо у вас немає часу написати хороший код, у вас набагато більші проблеми ...
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текст був більше схожий на приклад вище.

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


Як час виконання повинен знати, що є нульовим?
Буде

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

Погодився, і я б сказав те саме KeyNotFoundExceptionі для багатьох інших
прикрощів

Насправді, у випадку нульової перенаправлення це виглядає як обмеження у способі відхилення від CLR (завдяки коментарю Шахроза Джефрі до питання ОП)
sinelaw


4

Ваш журнал повинен містити стек стека - що зазвичай дає вам підказку щодо того, який рядок у методі має проблему. Можливо, вам потрібно буде зробити так, щоб ваша версія випуску включала символи PDB, щоб ви мали уявлення, на якому рядку вказана помилка.

Звичайно, це не допоможе вам у цьому випадку:

Foo.Bar.Baz.DoSomething()

Принцип " Tell not ask" може допомогти уникнути такого коду.

Щодо того, чому інформація не включена, я не впевнений - я підозрюю, що принаймні у налагоджувальній збірці, якщо вони справді цього захотіли, вони могли б це зрозуміти. Виконання аварійного дампа і відкриття в WinDBG може допомогти.


2

Виключення були створені як інструмент для передачі сигналу про надзвичайні не фатальні умови в ланцюзі викликів. Тобто вони не розроблені як інструмент налагодження.

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

Однак виняток з нульовим покажчиком розроблено як дійсне умова виконання, яке можна перекинути і потрапити в звичайний потік програми. Отже, міркування щодо ефективності потрібно враховувати. І будь-яке налаштування повідомлення про виключення вимагає створення рядкових об'єктів, об'єднаних та знищених під час виконання. Таким чином, статичне повідомлення, безперечно, швидше.

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


1

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

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

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


Ви розумієте, що люди пропонували подібні виправдання для написання асемблерною мовою? А не використовуєте автоматичний збір сміття? А вміти робити арифметику - в шістнадцятковій? "обтяжливий, стомлюючий і часом нудний" - це те, як ми описуємо завдання, які слід автоматизувати.
Spike0xff

0

Навчіться користуватися налагоджувачем. Це саме та річ, для якої призначена. Встановіть точку перерви у відповідному методі та відійдіть.

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

Редагувати: Чесно кажучи, я шокований тим, що ще ніхто не згадав про використання налагоджувача.


2
Отже, ви говорите, що всі винятки можна легко відтворити при використанні налагоджувача?
jgauffin

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

1
Отже, ви говорите, що у нас завжди доступ до несправного комп'ютера і що налагодження краще, ніж одиничні тести?
jgauffin

@jgauffin Я кажу, якщо у вас є вибір, тоді перейдіть з налагоджувачем, віддаючи перевагу іншим інструментам. Звичайно, якщо у вас немає цього вибору, то це трохи не-стартер. Зрозуміло, що це не так з цим питанням, саме тому я дав відповідь, яку я зробив. Якби питання полягало в тому, як виправити цю проблему на клієнтському комп'ютері, не маючи можливості робити віддалену налагодження (або локальну налагодження), моя відповідь була б іншою. Ви, схоже, намагаєтесь викривити мою відповідь способами, які не мають стосунку до питання.
Кромулент

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>

Зауважте: зараз у # 6 є nameofваш фрагмент, throw new ArgumentNullException(nameof($argument$))який має переваги: ​​не включаючи магічні константи, перевіряючи їх компілятором, а також краще працювати з інструментами рефакторингу
stijn
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.