Аргументи за чи проти використання Try / Catch як логічних операторів [закрито]


36

Я щойно відкрив чудовий код у програмі наших компаній, яка використовує блоки Try-Catch як логічні оператори.
Значить, "зробіть якийсь код, якщо він видає цю помилку, зробіть цей код, але якщо це кине цю помилку, зробіть замість цього 3-ю річ".
Він використовує "Нарешті" як вислів "else", який з'являється.
Я знаю, що це по суті неправильно, але перед тим, як піти в бій, я сподівався на кілька добре продуманих аргументів.
І ей, якщо у вас є аргументи ЗА використання цього способу Try-Catch, будь ласка, скажіть.

Для всіх, хто цікавиться, мова - це C #, а код, про який йде мова, становить близько 30+ рядків і шукає конкретні винятки, він не обробляє ВСІ винятки.


13
У мене є лише аргументи проти цієї практики, і оскільки ви вже здаєтеся впевненими, що це погана ідея, я не збираюся розміщувати їх.
FrustratedWithFormsDesigner

15
@FrustratedWIthFormsDesigner: Це погані міркування. Що з людьми, які не переконані? Як щодо мого актуального запитання, де я конкретно задаю причини, оскільки я не можу реально сказати "чому", тільки що я знаю, що це неправильно.
Джеймс П. Райт

3
Моя думка дуже залежить від того, що насправді робить кодекс. Є речі, які не можна перевірити заздалегідь (або дозволити умови гонки при спробі, наприклад, багато файлових операцій - затримка між чеком і операцією, з файлом може статися все, що завгодно) і має бути try'd. Не кожен винятковий випадок, який вимагає виключення в цілому, повинен бути фатальним у цьому конкретному випадку. Отже, чи могли ви це зробити простішим, рівним чи більш надійним способом, не використовуючи винятків?

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

2
Якою мовою вони користуються? Це абсолютно добре, скажімо, в OCaml, де стандартна бібліотека викидає винятки звичайно. Обробка винятків там дуже дешева. Але це не буде настільки ефективно в CLI або JVM.
SK-логіка

Відповіді:


50

Обробка винятків, як правило, є дорогим способом керування потоком (звичайно, для C # та Java).

Виконання виконує досить багато роботи, коли будується об'єкт виключення - збирання сліду стека разом, з'ясування місця оброблення виключення та багато іншого.

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

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

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


1
Хороший момент щодо продуктивності, хоча це також залежатиме від специфіки платформи.
FrustratedWithFormsDesigner

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

5
@delnan - Ні, не єдиний недолік.
Oded

2
Крім слова непередбаченого, я згоден з вашим дописом. У вашому коді є випадки, коли трапляються винятки. Більшість з них має бути передбачуваним, як, наприклад, збої в з'єднанні з БД або викликом служби або будь-коли, коли ви маєте справу з некерованим кодом. Ви ловите проблеми поза вашим контролем і вирішуєте їх. Але я погоджуюся, що ви ніколи не повинні мати контроль потоку на основі винятків, яких ви можете уникнути у своєму коді.
SoylentGray

4
"Обробка винятків, як правило, є дорогим способом управління потоком". Неправильно для Python.
S.Lott

17

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

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

try { 
    return Optional.of(Long.valueOf(s)); 
} catch (NumberFormatException) { 
    return Optional.empty(); 
}

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

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

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


4
Я думаю, що це насправді досить хороший аргумент. Ваш код чистий і стислий і має сенс у тому, що він робить. Це насправді схоже на те, що відбувається в коді, який я бачу, тільки він набагато складніший, вкладений і охоплює 30+ рядків коду.
Джеймс П. Райт

@James: звучить так, що код буде добре, якби ви витягли кілька менших методів.
кевін клайн

1
Можна стверджувати, що Java дійсно може використовувати щось більше, на зразок TryParse C #. У C # цей код був би int i; if(long.TryParse(s, out i)) return i; else return null;. TryParse повертає значення false, якщо рядок не вдалося розібрати. Якщо його можна було проаналізувати, він встановлює вихідний аргумент на результати розбору та повертає істину. Ніяких винятків не відбувається (навіть внутрішньо в TryParse), і особливо немає винятків типу, що означає помилку програміста, як .NETs FormatExceptionзавжди вказує.
Кевін Кеткарт

1
@Kevin Cathcart, але чому це краще? код, який ловить NumberFormatException, для мене набагато очевидніший, ніж перевірка результату TryParse на нуль.
Вінстон Еверт

1
@ChrisMiskowiec, я підозрюю, який з них вам зрозуміліший - це майже те, до чого ви звикли. Мені здається, що ловити виняток набагато простіше, ніж слідкувати, а потім перевіряти магічні значення. Але це все суб’єктивно. Об'єктивно, я думаю, що ми повинні віддавати перевагу виняткам, оскільки вони мають здоровий дефолт (збій програми), а не дефолт, який приховує помилку (продовжуйте так, ніби нічого не сталося). Це правда, що .NET працює погано за винятками. Але це результат дизайну .NET, а не характеру винятків. Якщо в .NET, так, ви повинні це зробити. Але в принципі, винятки є кращими.
Вінстон Еверт

12

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

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

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

  • Труднощі з налагодженням Іноді корисна (при спробі налагодження програми) інформація викидається у вікно, коли трапляються винятки. Ви можете втратити інформацію про стан програми або історію, що є важливою для розуміння поведінки часу виконання

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

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


10

Я бачив цю схему, яку використовували кілька разів.

Є дві основні проблеми:

  • Це надзвичайно дорого (інстанція об'єкта виключення, збирання стека викликів тощо). Деякі компілятори можуть насправді оптимізувати його, але я б не розраховував на це в цьому випадку, оскільки винятки не призначені для такого використання, тому ви не можете очікувати, що люди оптимізують його.
  • Використання винятків для контрольного потоку насправді є стрибком, як і goto. Стрибки вважаються шкідливими з ряду причин. Їх слід використовувати, якщо всі альтернативи мають значні недоліки. Насправді, у всьому своєму коді я згадую лише 2 випадки, коли стрибки були явно найкращим рішенням.

2
Просто виставляючи його там, якщо / ще також вважається стрибком. Різниця полягає в тому, що це стрибок, який може відбутися лише в цьому конкретному місці (і воно читається набагато простіше), і вам не доведеться турбуватися про назви етикетки (чого вам не потрібно робити і при спробі / ловінні) , але порівнюючи до goto). Але просто кажучи "це стрибок, який поганий" також говорить, що якщо / інакше погано, коли це не так.
jsternberg

1
@jsternbarg: Так, якщо / else насправді компілюється для стрибків на рівні машини, але на мовному рівні, ціль стрибка - це наступне твердження, тоді як у випадку циклів - це поточне твердження. Я б заперечував, що це більше крок, ніж стрибок;)
back2dos

9

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

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

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

Все, що сказано, використання виключень має бути обмежене з кількох причин:

  1. Випадкове ковтання вкладених винятків. Це головне. Якщо ви потрапили на якийсь глибоко вкладений виняток, з яким намагається вчинити хтось інший, ви просто застрелили свого колегу-програміста (можливо, навіть ви!) У ногу.
  2. Тести стають неочевидними. Блок коду, у якому є виняток, може мати одну з декількох речей, що не так. З іншого боку, булева ситуація, хоча теоретично це може дратувати налагодження, це, як правило, не так. Це особливо вірно, оскільки try...catchприхильники потоку управління загалом (на мій досвід) не дотримуються філософії "мінімізувати код у пробному блоці".
  3. Це не дозволяє else ifблокувати. Досить сказано (і якщо хтось зустрічається з "Але вони могли використовувати різні класи винятків", моя відповідь "Зайдіть у свою кімнату і не виходьте, поки не подумаєте про те, що ви сказали".)
  4. Це граматично вводить в оману. Exception, для решти світу (як і для не прихильників try...catchфілософії контрольного потоку) означає, що щось увійшло в нестабільний (хоча можливо, відновлений) стан. Нестабільні стани - БАД, і це повинно нас тримати в ночах, якщо насправді є виключення, які можна уникнути (це насправді повзує мене, немає брехні).
  5. Він не відповідає загальному стилю кодування. Наша робота, як з нашим кодом, так і з нашим інтерфейсом, полягає в тому, щоб зробити світ максимально очевидним. try...catchконтрольний потік суперечить тим, що зазвичай вважається найкращими методами. Це означає, що знадобиться більше часу, щоб хтось новий у проекті вивчив проект, а це означає збільшення кількості людино-годин за абсолютно ніякого прибутку.
  6. Це часто призводить до дублювання коду. Нарешті блоки, хоч це і не є строго необхідним, потрібно вирішити всі звисаючі покажчики, які залишилися відкритими перерваним блоком спробу. Тому спробуйте блоки. Це означає, що ви можете мати try{ obj.openSomething(); /*something which causes exception*/ obj.doStuff(); obj.closeSomething();}catch(Exception e){obj.closeSomething();}. За більш традиційним if...elseсценарієм, closeSomething()менша ймовірність (знову ж таки, особистий досвід) буде копіювати та вставляти завдання. (Правда, цей конкретний аргумент більше стосується людей, яких я зустрів, ніж власне філософії).

AFAIK, №6 - проблема лише в Java, яка, на відміну від будь-якої іншої сучасної мови, не має ні закриття, ні управління ресурсами на основі стека. Це, звичайно, не проблема, притаманна використанню винятків.
кевін клайн

4 і 5, здається, однакові для мене. 3-6 не застосовуються, якщо ваша мова - пітон. 1 і 2 - це потенційні проблеми, але я думаю, що з ними вирішується мінімізація коду у блоці спробу.
Вінстон Еверт

1
@Winston Я не згоден. 1 і 2 - основні проблеми, які, хоча і зменшуються кращими стандартами кодування, все ж задають питання, чи не може бути краще просто уникнути потенційної помилки для початку. 3 застосовується, якщо ваша мова - Python. 4-5 також можуть подати заявку на Python, але вони не повинні. 4 і 5 - це дуже різні моменти - введення в оману відрізняється від стилістичних відмінностей. Оманливий код може робити щось на кшталт назви тригера для запуску транспортного засобу "кнопка зупинки". Стилістично, з іншого боку, це також може бути аналогічним уникненню всіх блокових відступів у C.
cwallenpoole

3) У Python є ще блок для виключень. Це дуже корисно для уникнення проблем, які ти зазвичай отримуєш для 1 і 2.
Вінстон Еверт

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

4

Мій головний аргумент полягає в тому, що використання try / catch для логіки порушує логічний потік. Дотримуватися «логіки» через нелогічні конструкції (для мене) контрінтуїтивно та заплутано. Я звик читати свою логіку як "якби стан, то інший B". Читання того самого твердження, що і "Спробуйте зловити, тоді виконайте B", виглядає дивно. Було б навіть дивніше, якщо оператор A- це просте завдання, вимагаючи додаткового коду, щоб примусити виняток, якщо conditionвін помилковий (і для цього, ймовірно, потрібен буде ifзаява в будь-якому разі).


2

Ну, лише питання етикету, перед тим, як "розпочати з ними суперечку", я, як ви заявляєте, запитав "Чому вони використовують обробку виключень у всіх цих різних місцях?"

Я маю на увазі, є кілька можливостей:

  1. вони некомпетентні
  2. є цілком поважна причина того, що вони зробили, яке може не з’явитися з першого погляду.
  3. іноді це питання смаку і може спростити деякий складний потік програми.
  4. Це пережиток минулого, яке ще ніхто не змінив, і вони будуть раді, якщо хтось це зробить

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


1

Спробуйте Catch слід використовувати лише для обробки виключень. Тим більше, що специфічна обробка виключень. Ваш спробу лову повинен охоплювати лише очікувані винятки, в іншому випадку він недостатньо сформований. Якщо вам потрібно скористатися уловом, будь-який спробувати, то ви, ймовірно, робите щось не так.

Редагувати:

Причина: Ви можете стверджувати, що якщо ви використовуєте спробу catch в якості умовного оператора, то як ви збираєтеся враховувати РЕАЛЬНІ винятки?


2
ОП просить причини / аргументи. Ви не надали жодної.
Одід

"Ви можете стверджувати, що якщо ви використовуєте спробу catch в якості умовного оператора, то як ви збираєтесь обліковувати НЕПОЗНАЧНІ винятки?" - Уловлюючи ці реальні винятки в catchпункті, відмінному від того, що сприймає "нереальні" винятки?

@delnan - Дякую! Ви довели, що я збирався зробити. Якби людина відповіла на те, що ви щойно сказали, і на мою причину, і на Одеда, я би просто перестав говорити, тому що він не шукає причин, він просто впертий, і я не витрачаю час на впертість.
AJC

Ти називаєш мене studdborn, тому що я вказую на недоліки в деяких перерахованих тут аргументах? Зауважте, як я нічого не можу сказати проти інших мов, викладених тут. Я не прихильник використання винятків для контролю потоку (хоча я нічого не засуджую і без деталей, звідси мій запит на розробку в ОП), я просто граю захисника диявола.

1
Використання try-catch як умовного оператора жодним чином не заважає йому працювати, щоб також ловити "реальні" винятки.
Вінстон Еверт

1

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


1
Але чому? Суть питання в тому, чому (або чому ні) винятки корисні лише для цього.
Вінстон Еверт

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

1
-1: тавтологічне твердження
печиво з рисової муки

1

Причина використовувати винятки:

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

Причини не використовувати винятки:

  1. Багато мов не розроблені для швидкого винятку.
  2. Винятки, які пересуваються на кілька шарів до стека, можуть залишати непослідовний стан

Наприкінці:

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


0

Я використовую try-> catch як контроль потоку в певних ситуаціях. Якщо ваша основна логіка залежить від того, що виходить з ладу, але ви не хочете просто кинути виняток і кинути ... Це те, для чого потрібен блок "try-> catch". Я пишу безліч непідконтрольних unix-скриптів на стороні сервера, і для них набагато важливіше, щоб вони не вийшли з ладу, ніж це, щоб вони вийшли з ладу.

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


0

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


"Стандарт C ++ полягає в тому, що винятки слід зберігати для справді виняткових подій." Це нісенітниця. Навіть винахідник мови не погоджується з вами .
arayq2

-1

Це погана звичка, але я це час від часу роблю в пітоні. Як і замість перевірки, чи існує файл, я просто намагаюся його видалити. Якщо це кидає виняток, я ловлю і просто рухаюся, знаючи, що його там не було. <== (не обов'язково істинно, якщо інший користувач є власником файлу, але я це роблю в домашній каталог користувача, щоб це НЕ МОЖЕ відбуватися).

І все-таки, надмірне використання - це погана практика.


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

Якщо python кидає виняток лише тому, що файл не існує, це, здається, заохочує шкідливі звички, як описує delnan.
Пер Йоханссон

@delnan: Хоча правда, IMHO, якщо вірогідний запуск таких умов гонки, вам слід переосмислити свій дизайн. У такому випадку у ваших системах явно не вистачає розділення проблем. Якщо у вас є дуже вагомі причини зробити так саме, вам слід або уніфікувати доступ на своєму кінці, або використовувати належну базу даних для обробки декількох клієнтів, які намагаються здійснювати транзакції на одних і тих же постійних даних.
back2dos

1
Це не погана звичка. Його рекомендований стиль в Python.
Вінстон Еверт

@ back2dos, чи можете ви запобігти цьому при роботі із зовнішніми ресурсами, такими як інші користувачі бази даних / файлової системи?
Вінстон Еверт

-1

Мені подобається, як Apple це визначає : винятки стосуються лише помилок програміста та фатальних помилок виконання. Ще використовувати коди помилок. Це допомагає мені вирішити, коли його використовувати, думаючи собі: "Це помилка програміста?"


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