Чи слід перевіряти на null, якщо він не очікує нуля?


113

Минулого тижня ми мали бурхливий аргумент щодо обробки нулів на рівні обслуговування нашого додатка. Питання в контексті .NET, але це буде те саме в Java та багатьох інших технологіях.

Питання полягало в тому, що: чи слід завжди перевіряти наявність нулів і змушувати код працювати незалежно від того, чи пускати виключення, коли нуль несподівано отримано?

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

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

Якої практики ви дотримуєтесь і які ваші аргументи за чи проти будь-якого підходу?

Оновлення:

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

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

Гіпотетичною перевагою було б те, що сторінка продовжить працювати. Подумайте про результати пошуку на Stack Exchange у різних групах (користувачі, коментарі, питання). Метод може перевірити наявність нуля та перервати обробку користувачів (що через помилку є нульовим), але повернути розділи "коментарі" та "питання". Сторінка буде продовжувати працювати, за винятком того, що не буде відсутній розділ "користувачі" (це помилка). Чи повинні ми провалитись рано та зламати всю сторінку чи продовжувати працювати і чекати, коли хтось помітить, що розділ «користувачі» відсутній?


62
Принцип Фокс Малдера: Нікому не довіряйте.
янніс

14
@YannisRizos: цей принцип хороший - він настільки хороший, що творці Java та C # дозволяють мовному середовищу виконувати це автоматично. Тому зазвичай немає необхідності робити явні перевірки на наявність нуля скрізь у коді цими мовами.
Док Браун


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

2
assert(foo != null, "foo is web control within the repeater, there's no reason to expect it to be null, etc, etc...");
zzzzBov

Відповіді:


105

Питання не стільки в тому, чи слід перевіряти nullчи дозволяти виконувати час винятку; саме так слід реагувати на таку несподівану ситуацію.

Тоді ваші варіанти:

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

Не існує загального правила, яке з них є найкращим рішенням. Особисто я б сказав:

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

1
@Stilgar для enduser різниці немає. для розробника специфічний виняток, наприклад "сервер баз даних недоступний", більш інтуїтивний, що "null pointer excepteion"
k3b

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

1
@Stilgar: Тут є дві проблеми: поведінка без догляду та ремонтопридатність. Хоча для поведінки без нагляду є невелика різниця між конкретним винятком та загальним, для ремонтопридатності це може змінити значення "о, так ось чому все підірветься" та багатогодинного налагодження.
tdammers

3
tdammers - це точно так. "Посилання на об'єкт не встановлено на екземпляр об'єкта" - одне з найбільш дратівливих винятків, які я бачу з найбільшою частотою. Я швидше побачив би конкретний виняток, чи це ArgumentNullException з назвою параметра, або спеціальним винятком "Немає запису повідомлення для користувача TK421."
Шон Чейз

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

58

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


Замість цього ArgumentNullExceptionслід використовувати кодові контракти (якщо тільки це вихідний код до 2010 року, який не підтримує кодові контракти).
Арсеній Муренко

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

9
якщо я читаю питання правильно, кидання ArgumentNullExceptionвважається "обробкою" нульового значення: воно запобігає подальшому виключенню нульової посилання.
KutuluMike

@MichaelEdenfield: Я не думаю, що це вважається справжнім керуванням відповідно до даного питання (наша мета - не зробити так, щоб ваш код працював незалежно від того ). Якщо чесно, я часто забуваю написати відповідний блок, який буде кинутий, якщо нуль буде передано. Однак я вважаю, що викидання NullArgumentException є найкращою практикою, і найгіршою практикою, на мій погляд, є написання коду, який не може обробити null, а замість того, щоб викидати винятки, наприклад, інший null як результат методу (або порожня рядок, або порожня колекція, або -1 або пропускає виконання методу, якщо тип повернення недійсний тощо).
empi

2
@Giorgio, ArgumentNullExceptionдає зрозуміти, в чому проблема, і як її усунути. C # не має тенденції використовувати "невизначений". Якщо ви називаєте щось таким, що не визначено, то спосіб, яким ви його називаєте, не має сенсу. Виняток - правильний спосіб вказати це. Тепер іноді я буду робити методи аналізу, які повертаються в нуль, якщо int(наприклад) неможливо проаналізувати. Але це випадок, коли нерозбірливий результат - це законна можливість, а не помилка використання. Дивіться також цю статтю .
Kyralessa

35

Звичка перевіряти nullмій досвід походить від колишніх розробників C або C ++. На цих мовах ви маєте хороший шанс приховати серйозну помилку, не перевіряючи NULL. Як відомо, у Java або C # речі різні, використання нульового відліку без перевірки nullпризведе до винятку, таким чином помилка не буде таємно прихована, доки ви не будете її ігнорувати на стороні виклику. Тому в більшості випадків перевірка прямо на nullце не має особливого сенсу і надмірно ускладнює код більше, ніж потрібно. Тому я не вважаю, що перевірка нуля є доброю звичкою в цих мовах (принаймні, не в цілому).

Звичайно, є винятки з цього правила, ось які я можу придумати:

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

  • ви хочете, щоб ваша програма "провалювалася більш рано". Наприклад, ви хочете перевірити наявність нульового значення параметра конструктора, де посилання на об'єкт в іншому випадку зберігатиметься лише в змінній члена і використовуватиметься пізніше.

  • Ви можете вирішити ситуацію з нульовим виправленням безпечно, без ризику наступних помилок та без маскування серйозної помилки.

  • ви хочете зовсім іншого типу сигналізації про помилку (наприклад, повернення коду помилки) у вашому поточному контексті


3
"Ви хочете кращого, зрозумілого для людини повідомлення про помилку" - це найкраща причина, щоб це зробити, на мою думку. "Посилання на об'єкт, не встановлене на екземпляр об'єкта" або "Недійсне посилання на виняток", ніколи не є настільки корисним, як "Продукт не призначений для використання"
пдр

@pdr: Ще одна причина перевірки - переконатися, що вона завжди виявляється.
Джорджіо

13
"Колишні розробники C", можливо. "Колишні розробники C ++", лише якщо вони самі відновляють розробників C. Як сучасний розробник C ++, я б дуже підозріло ставився до коду, який використовує голі покажчики або необдуманий динамічний розподіл. Насправді, я б спокусився перевернути аргумент і сказати, що C ++ не зазнає проблеми, яку описує ОП, оскільки його змінні не мають додаткового спеціального властивості null-ness, яке мають Java та C #.
Керрек СБ

7
Ваш перший абзац - це лише половина історії: так, якщо ви використаєте нульову посилання в C #, ви отримаєте виняток, тоді як у C ++ ви отримаєте невизначене поведінку. Але у добре написаному C # ви застрягли з набагато більшими нульовими посиланнями, ніж у добре написаних C ++.
Джеймс Макнелл

Чим краще інкапсуляція, тим краще ця відповідь застосовується.
radarbob

15

Використовуйте твердження для тестування та вказівки попередніх та пост-умов та інваріантів свого коду.

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

Затвердження, IMO - це відповідна перевірка, оскільки це:

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

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

Оновлення

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

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


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

1
Ну, не соромтеся використовувати функцію затвердження ^ h ^ h ^ hcond умовного кидання, яка також активна у випуску, якщо вам це потрібно. Я свою катаю досить часто.
Макке

7

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


28
Сподіваюсь, ви мали на увазі: Невдача рано, невдача швидко, не часто ...
empi

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

@MichaelBorgwardt Це не те, для чого призначені сліди стека?
R0MANARMY

6
@ R0MANARMY: сліди стека не допомагають, коли нульове значення зберігається в якомусь полі, а NullPointerException викидається набагато пізніше.
Майкл Borgwardt

@ R0MANARMY, навіть якщо вам вдалося довірити номер рядка у сліді стека (у багатьох випадках ви не можете), деякі рядки містять кілька змінних, які можуть бути нульовими.
Охад Шнайдер

6
  • Перевірка нуля має бути вашою другою природою

  • Ваш API буде використовуватися іншими людьми, і їх очікування, ймовірно, буде відрізнятися від вашого

  • Ретельні випробування блоку зазвичай підкреслюють необхідність перевірки нульової перевірки

  • Перевірка на наявність нулів не означає умовних тверджень. Якщо ви переживаєте, що перевірка нуля робить ваш код менш читабельним, то, можливо, ви захочете розглянути контракти з кодом .NET.

  • Спробуйте встановити ReSharper (або подібний) для пошуку у вашому коді для відсутності перевірки нульових довідок


Використання кодових контрактів для передумов - це просто прославлені умовні висловлювання.
CVn

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

4

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

Якщо функція або метод foo () має аргумент x типу T, x може бути обов'язковим або необов'язковим .

У C ++ ви можете передати обов'язковий аргумент як

  • Значення, наприклад, недійсний foo (T x)
  • Посилання, наприклад, недійсний foo (T & x).

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

Якщо ви передаєте необов'язковий аргумент, ви можете використовувати вказівник і використовувати значення NULL, щоб вказати, що значення не задано:

void foo(T* x)

Альтернативою є використання розумного вказівника на зразок

void foo(shared_ptr<T> x)

У цьому випадку ви, ймовірно, хочете перевірити вказівник у коді, оскільки він має значення для методу foo (), чи містить x значення чи взагалі немає значення.

Третій і останній випадок - ви використовуєте вказівник для обов'язкового аргументу. У цьому випадку виклик foo (x) з x == NULL є помилкою, і вам потрібно вирішити, як з цим впоратися (те саме, що з індексом поза межами діапазону або будь-яким недійсним введенням):

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

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

У Java у вас менше варіантів, оскільки всі об'єкти передаються за допомогою посилань, які завжди можуть бути нульовими. Знову ж таки, якщо null представляє значення NONE необов’язкового аргументу, то перевірка нуля (ймовірно) буде частиною логіки реалізації.

Якщо null є недійсним вводом, ви маєте помилку, і я б застосував аналогічні міркування, як і у випадку вказівників C ++. Оскільки в Java ви можете зафіксувати винятки з нульових покажчиків, підхід 1 вище еквівалентний підходу 2, якщо метод foo () відкидає всі вхідні аргументи у всі шляхи виконання (що, мабуть, трапляється дуже часто для малих методів).

Підбиття підсумків

  • І в C ++, і в Java, перевірка того, чи необов'язкові аргументи є недійсними, є частиною логіки програми.
  • У C ++ я завжди перевіряв би обов’язкові аргументи, щоб переконатися, що належним винятком є ​​викид, щоб програма могла обробляти це надійно.
  • У Java (або C #) я би перевіряв обов'язкові аргументи, якщо є шляхи виконання, які не будуть кидатися, щоб мати більш сильну форму "невдало прострочити".

EDIT

Дякуємо Stilgar за більш детальну інформацію.

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

Отже, у вас метод m1 () викликає метод m2 (), а m2 () повертає посилання (на Java) або покажчик (на C ++) на якийсь об'єкт.

Що таке семантика m2 ()? Чи повинен m2 () завжди повертати ненульовий результат? Якщо це так, то нульовим результатом є внутрішня помилка (у м2 ()). Якщо ви перевірите значення повернення в m1 (), ви можете мати більш надійну обробку помилок. Якщо цього не зробити, ви, мабуть, рано чи пізно матимете виняток. Можливо, не. Сподіваємось, що виняток буде видалено під час тестування, а не після розгортання. Питання : якщо m2 () ніколи не повертає null, чому він повертає null? Може, замість цього слід викинути виняток? m2 (), ймовірно, баггі.

Альтернативою є те, що повернення null є частиною семантики методу m2 (), тобто має необов'язковий результат, який повинен бути задокументований у документації Javadoc методу. У цьому випадку нормально мати нульове значення. Метод m1 () повинен перевірити його як будь-яке інше значення: тут немає помилок, але, ймовірно, перевірка, чи результат нульовий, є лише частиною логіки програми.


У нашому випадку аргумент був недійсним. Ми здійснили дзвінок у базі даних щодо того, що, як очікувалося, буде присутнє, але на практиці його немає. Питання полягає в тому, чи слід перевірити, чи існує кожен об’єкт, чи слід перевірити, лише якщо ми очікуємо, що він відсутній.
Stilgar

Отже результат, повернутий методом, був посиланням на об'єкт, а null - значення, яке використовується для представлення результату: NOT FOUND. Я правильно розумію?
Джорджіо

Я оновив питання з додатковими подробицями.
Stilgar

3

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

Якщо ви можете продовжувати безпечно, замінивши інше значення (порожня колекція, скажімо, або значення за замовчуванням або екземпляр), то будь-якими способами зробіть це. @ Приклад Шахбаза із World of Warcraft про заміну одного зображення на інший потрапляє до цієї категорії; саме зображення, ймовірно, просто прикраса і не має функціонального впливу. (У складанні налагодження я б використовував кричущий колір, щоб звернути увагу на те, що відбулася заміна, але це дуже залежить від характеру програми.) Хоча, введіть деталі про помилку, щоб ви знали, що вона виникає; в іншому випадку ви будете приховувати помилку, про яку, ймовірно, хочете дізнатися. Сховати його від користувача - одне (знову ж таки, якщо припустити, що обробка може тривати безпечно); приховати це від розробника зовсім інше.

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

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


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

@Stilgar, якщо немає кому перевірити журнали, то це не проблема існування чи відсутності нульових перевірок.
CVn

2
Є проблема. Кінцеві користувачі можуть не помітити, що система не працює належним чином протягом тривалого часу, якщо є нульові перевірки, які просто приховують помилку і записують її в журнали.
Stilgar

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

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

2

Звичайно NULL, не очікується , але помилки завжди є!

Я вважаю, ви повинні перевірити, NULLчи не очікуєте ви цього. Дозвольте навести приклади (хоча їх немає на Java).

  • У «World of Warcraft» через помилку зображення одного з розробників з’явилося на кубі для багатьох об’єктів. Уявіть, якби графічний движок не очікував NULL покажчиків, стався б збій, в той час як це перетворилося на не дуже фатальний (навіть смішний) досвід для користувача. Щонайменше, ви несподівано б не втратили зв’язок або будь-яку зі своїх точок збереження.

    Це приклад, коли перевірка на NULL запобігла аварію, і справа мовчки розглядалася.

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

    Під "проблемою" я маю на увазі збій (як і в аварії аварії (скажімо, програма написана на C ++), а не виключення з нульовим покажчиком). У цьому випадку транзакцію потрібно відмовити, якщо виникає NULL (що, звичайно, було б неможливо, якби ви не перевіряли змінні на NULL!)

Я впевнений, що ви зрозуміли ідею.

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

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


Ваш другий приклад спростує вашу думку. У банківській операції, якщо щось піде не так, транзакцію слід перервати. Саме тоді, коли ви покриваєте помилку, гроші можна втратити чи дублювати. Перевірка нуля буде такою, як "Клієнт надсилає нульові гроші ... ну я просто надішлю $ 10 (еквівалент образу розробника)"
Stilgar

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

2
Вся суть "транзакції" полягає в тому, що вона не може бути наполовину завершена :) Якщо є помилка, вона повертається назад.
Stilgar

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

1
@ MerlynMorgan-Graham, я так і зробив. Сподіваюсь, що очистить плутанину.
Шахбаз

2

Як зазначали інші відповіді. Якщо ви цього не очікуєте NULL, дайте зрозуміти, кидаючи ArgumentNullException. На мою думку, коли ви налагоджуєте проект, це допоможе вам швидше виявити недоліки в логіці вашої програми.

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

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


1

Кожен nullу вашій системі бомба, що цокає чекає свого відкриття. Перевірте nullмежі, в яких ваш код взаємодіє з кодом, який ви не контролюєте, і забороніть nullв межах власного коду. Це позбавить вас від проблеми, не наївно довіряючи іноземному коду. Використовуйте натомість винятки або якийсь тип Maybe/ Nothingтип.


0

Хоча виняток з нульовим покажчиком з часом буде викинуто, він часто буде викинутий далеко від точки, де ви отримали нульовий покажчик. Зробіть собі прихильність і скоротіть час, який потрібен для виправлення помилки, принаймні зафіксувавши факт, що ви якнайшвидше наберете нульовий покажчик.

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


-1

Оскільки, Fail early, fail fastяк зазначає @DeadMG (@empi), це неможливо, оскільки веб-додаток повинен добре працювати під помилками, ви повинні застосувати правило

не виняток лову без реєстрації

тож ви як розробники знаєте про можливі проблеми, перевіряючи журнали.

якщо ви використовуєте систему реєстрації журналів, наприклад log4net або log4j, ви можете встановити додатковий спеціальний вихідний запис (він же додаток), який надсилає вам повідомлення про помилки та fatalerrors. тож ви будьте в курсі .

[оновлення]

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

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

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


1
Питання в тому, що відбувається зі сторінкою?
Стілгар

Як ми знаємо, що дані мають сенс і система перебуває в послідовному стані. Зрештою, помилка була несподіваною, тому ми не знаємо, який вплив був.
Stilgar

"Оскільки Fail рано, невдача швидко, як заявлено @DeadMG (@empi), неможлива, оскільки веб-додаток повинен добре працювати під помилками, ви повинні застосувати правило": Ви завжди зможете ловити всі винятки (catch (Throwable t)) і переадресація на якусь сторінку помилок. Хіба це не було б можливо?
Джорджо

-1

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

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

Більше того, якщо хтось повинен працювати з вашими твердженнями коду, розповість йому про ваші припущення, які ви зробили під час проектування / написання вашого коду (у цьому випадку: Ей чувак, цей об’єкт повинен бути представлений!), Що полегшує його підтримку.


Чи можете ви написати щось проголосувати? Я би вдячний для обговорення. Thanx
GeT

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

-1

Я звичайно перевіряю наявність нулів, але я "обробляю" несподівані нулі, кидаючи виняток, який дає трохи більше деталей про те, де саме відбулася нуль.

Редагувати: Якщо ви отримаєте нуль, коли не очікуєте, що щось не так - програма повинна померти.


-1

Це залежить від мовної реалізації винятків. Винятки дозволяють вжити певних дій, щоб запобігти пошкодженню даних або чистому звільненню ресурсів у випадку, якщо ваша система переходить у непередбачуваний стан чи стан. Це відрізняється від помилок, які є умовою, яку ви можете передбачити. Моя філософія полягає в тому, що ви повинні мати якомога менше обробку винятків. Це шум. Чи може ваш метод зробити щось розумне, обробляючи виняток? Чи може вона відновитися? Чи може це відремонтувати чи запобігти подальшій шкоді? Якщо ви просто збираєтеся спіймати виняток, а потім повертаєтесь із методу або не вдається іншим нешкідливим способом, тоді вилучити виняток справді не було сенсу. Ви б додали лише свій шум і складність своєму методу, і, можливо, ви ховали джерело несправності у вашій конструкції. У цьому випадку я б дозволив несправності сплисти і потрапив би у зовнішню петлю. Якщо ви можете зробити щось, наприклад, відремонтувати об'єкт або позначити його як пошкоджений або випустити якийсь критичний ресурс, наприклад, файл блокування або, чисто закривши сокет, це корисне використання винятків. Якщо ви дійсно очікуєте, що NULL часто відображатиметься як дійсний крайовий регістр, тоді вам слід обробляти це в нормальному потоці логіки за допомогою операторів if-the-else або case-case чи будь-чого іншого, а не обробкою винятків. Наприклад, поле, відключене у формі, може бути встановлено NULL, що відрізняється від порожнього рядка, що представляє поле, залишене порожнім. Це сфера, де ви повинні використовувати добру думку та здоровий глузд. Я ніколи не чув хороших твердих правил, як боротися з цією проблемою у будь-якій ситуації. Якщо ви можете зробити щось, наприклад, відремонтувати об'єкт або позначити його як пошкоджений або випустити якийсь критичний ресурс, наприклад, файл блокування або, чисто закривши сокет, це корисне використання винятків. Якщо ви дійсно очікуєте, що NULL часто відображатиметься як дійсний крайовий регістр, тоді вам слід обробляти це в нормальному потоці логіки за допомогою операторів if-the-else або case-case чи будь-чого іншого, а не обробкою винятків. Наприклад, поле, відключене у формі, може бути встановлено NULL, що відрізняється від порожнього рядка, що представляє поле, залишене порожнім. Це сфера, де ви повинні використовувати добру думку та здоровий глузд. Я ніколи не чув хороших твердих правил, як боротися з цією проблемою у будь-якій ситуації. Якщо ви можете зробити щось, наприклад, відремонтувати об'єкт або позначити його як пошкоджений або випустити якийсь критичний ресурс, наприклад, файл блокування або, чисто закривши сокет, це корисне використання винятків. Якщо ви дійсно очікуєте, що NULL часто відображатиметься як дійсний крайовий регістр, тоді вам слід обробляти це в нормальному потоці логіки за допомогою операторів if-the-else або case-case чи будь-чого іншого, а не обробкою винятків. Наприклад, поле, відключене у формі, може бути встановлено NULL, що відрізняється від порожнього рядка, що представляє поле, залишене порожнім. Це сфера, де ви повинні використовувати добру думку та здоровий глузд. Я ніколи не чув хороших твердих правил, як боротися з цією проблемою у будь-якій ситуації.

Java не спрацьовує у своїй винятку обробку з "перевіреними винятками", коли кожен можливий виняток, який може бути піднятий об'єктами, які використовуються в методі, повинен бути зафіксований або оголошений у пункті "кидки" методу. Це протилежність C ++ та Python, де ви можете обрати винятки, як вважаєте за потрібне. У Java, якщо ви модифікуєте тіло методу для включення виклику, який може викликати виняток, ви стикаєтесь з вибором додавання явного поводження за винятком, який може вам не хвилюватись, таким чином додаючи шум і складність у свій код, або потрібно додайте цей виняток до пункту "кидки" методу, який ви модифікуєте, який не тільки додає шуму і безладу, але змінює підпис вашого методу. Потім потрібно перейти до модифікаційного коду, де б ваш метод не використовувався для обробки нового винятку, тепер ваш метод може підняти або додати виняток до пункту "кидки" цих методів, що викликає ефект доміно змін коду. "Охоплення або уточнення вимоги" повинно бути одним з найбільш дратівливих аспектів Java. Виняток для виключення RuntimeExceptions та офіційна раціоналізація Java для цієї помилки дизайну - кульгавий.

Цей останній абзац мало стосунок до відповіді на ваше запитання. Я просто ненавиджу Java.

- Ной


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