Як допомагає кидання ArgumentNullException?


27

Скажімо, у мене є метод:

public void DoSomething(ISomeInterface someObject)
{
    if(someObject == null) throw new ArgumentNullException("someObject");
    someObject.DoThisOrThat();
}

Мене навчили вважати, що кидання ArgumentNullException"правильне", але помилка "Посилання на об'єкт не встановлено на екземпляр об'єкта" означає, що у мене є помилка.

Чому?

Я знаю, що якщо я кешував посилання на нього someObjectта використовував його пізніше, то краще перевірити наявність недійсності при передачі та невдачі достроково. Однак, якщо я перекреслюю це в наступному рядку, чому ми повинні робити перевірку? Виключення буде кинути так чи інакше.

Редагувати :

Мені просто прийшло в голову ... чи походить страх перед занепокоєнням нуля з такою мовою, як C ++, яка не перевіряє вас (тобто вона просто намагається виконати якийсь метод у місці пам'яті zero + method offset)?


Надання явного винятку допомагає підтвердити, що існує можливість помилки, яку можна безпосередньо зазначити в документації.
zzzzBov

Відповіді:


34

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

Крім того, що пізніше обслуговують люди, які надалі обслуговують? Додайте ArgumentNullException, якщо вони додають код до першої відміни, або просто додати код і пізніше дозволити кинути NullReferenceException?


Якщо, наприклад, це був непублічний метод, або публічний метод для непублічного класу, ви не вважаєте, що є вагома причина для перевірки нуля?
Скотт Вітлок

Зазвичай я не додаю ці перевірки до приватних методів, але все ж можу додати їх до публічних методів приватних класів, щоб вони були послідовними. Для приватних методів я схиляюсь до того, що аргументи перевірені раніше на якомусь рівні публічних викликів.
Метт Н

54

Мене навчили вважати, що кидання ArgumentNullException є "правильним", але помилка "Посилання на об'єкт не встановлено на екземпляр об'єкта" означає, що у мене є помилка. Чому?

Припустимо, я називаю метод, M(x)який ви написали. Я проходжу нульове. Я отримую ArgumentNullException з назвою "x". Цей виняток однозначно означає, що у мене помилка; Я не повинен був пропустити null для x.

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

Обидва винятки вказують на помилок; ніколи не слід кидати у виробничий код. Питання полягає в тому, який виняток повідомляє, хто має помилку краще для людини, яка виконує налагодження? Виняток Null deref майже нічого не говорить; Аргумент null виняток говорить вам багато. Будьте ласкаві до своїх користувачів; кидайте винятки, які дають змогу вчитися на своїх помилках.


Я не впевнений, що розумію, "neither should ever be thrown in production code"які ще типи коду / ситуацій вони б використовували? Чи слід їх складати так Debug.Assert? Або ви хочете сказати, не викидайте їх з API?
StuperUser

6
@StuperUser: Я думаю, ви неправильно зрозуміли, що я мав на увазі, тому що я написав це заплутано. Ви повинні мати throw new ArgumentNullExceptionсвій виробничий код, і він ніколи не повинен працювати за межами тестового випадку . Виняток ніколи насправді не слід кидати, тому що абонент ніколи не повинен мати цю помилку.
Ерік Ліпперт

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

5

Справа в тому, що ваш код повинен зробити щось значиме.

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

A ArgumentNullExceptionмає сенс, тому що він мені каже: операція не вдалася, оскільки аргумент someObjectбув null. Мені не потрібно дивитись на ваш код, щоб зрозуміти це.

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

Суть винятків полягає в повідомленні інформації у разі відмови, яку можна обробити під час виконання, щоб відновитись після відмови, або в час налагодження, щоб краще зрозуміти, що пішло не так.


2

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

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


1

Мене навчили вважати, що кидання ArgumentNullException є "правильним", але помилка "Посилання на об'єкт не встановлено на екземпляр об'єкта" означає, що у мене є помилка.

У вас виникла помилка, якщо код не працює за задумом. Система NullReferenceExceptionкидає систему, і цей факт справді робить більше помилки, ніж функції. Не тільки тому, що ви не визначили конкретний виняток, який потрібно обробляти. Також тому, що розробник, який читає ваш код, може припустити, що ви не врахували ситуацію, що має відбутися.

З аналогічних причин, що ApplicationExceptionналежить вашій програмі проти загальної, Exceptionяка належить до операційної системи. Тип викиду, який викидається, вказує, звідки походить виняток.

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

Нарешті, не нехтуйте здатністю абонента впоратися із ситуацією. Надаючи їм більш конкретний виняток, ArgumentExceptionвони можуть побудувати їх спробувати / ловити таким чином, щоб обробити цю помилку раніше, ніж загальну, NullReferenceExceptionяка може бути викликана будь-якою кількістю інших причин, що виникають в іншому місці, як, наприклад, неспроможність ініціалізувати змінну конструктора.


1

Перевірка робить більш чітким, що відбувається, але справжня проблема пов'язана з таким кодом (надуманий) приклад:

public void DoSomething(Foo someOtherObject, ISomeInterface someObject)
{
    someOtherObject.DoThisOrThat(this, someObject);
}

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

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


1

Ще одна причина, з якої слід враховувати, що робити, якщо деяка обробка відбувається до того, як буде використаний нульовий об'єкт?

Скажімо, у вас є файл даних про асоційовані ідентифікатори компанії (Cid) та ідентифікатори особи (Pid). Він зберігається як потік чисельних пар:

Cid Pid Cid Pid Cid Pid Cid Pid Cid Pid

І ця функція VB записує записи у файл:

Sub WriteRecord(Company As CompanyInfo, Person As PersonInfo)
    Using File As New StringWriter(DataFileName)
        File.Write(Company.ID)
        File.Write(Person.ID)
    End Using
End Sub

Якщо Personнульова посилання, рядок File.Write(Person.ID)буде винятком. Програмне забезпечення може зловити виняток і відновити, але це залишає проблему. Ідентифікатор компанії написаний без ідентифікатора асоційованої особи. Якщо насправді, під час наступного виклику цієї функції, ви збираєтесь поставити ідентифікатор компанії там, де очікували попередній ідентифікатор особи. Окрім того, що цей запис є невірним, кожен наступний запис матиме ідентифікатор особи, за яким слід ідентифікатор компанії, а це неправильний шлях.

Cid Pid Cid Pid Cid Cid Pid Cid Pid Cid

Перевіряючи параметри на початку функції, ви запобігаєте виникненню будь-якої обробки, коли метод гарантовано не працює.

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