Ефективне використання блоку спробувати / ловити?


21

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

Які побічні ефекти (якщо такі є) логіки написання в блоці лову?

Редагувати:

Я бачив клас Java SDK, в якому вони записали логіку всередині блоку лову. Наприклад (фрагмент, взятий з java.lang.Integerкласу):

        try {
            result = Integer.valueOf(nm.substring(index), radix);
            result = negative ? new Integer(-result.intValue()) : result;
        } catch (NumberFormatException e) {
            String constant = negative ? new String("-" + nm.substring(index))
                                       : nm.substring(index);
            result = Integer.valueOf(constant, radix);
        }

EDIT2 :

Я переглядав підручник, де вони вважають перевагою написання логіки виняткових випадків всередині винятків:

Винятки дають змогу писати основний потік коду та мати справу з винятковими випадками в інших місцях.

Будь-які конкретні вказівки, коли писати логіку в блок лову, а коли не робити?


12
@Coder Я думаю, що ви трохи переобладнали. Тим більше, що при багатьох існуючих API ви не можете цього уникнути.
CodesInChaos

8
@Coder: У Java винятки часто використовуються як механізм передачі сигналів, які є частиною законного потоку коду. Наприклад, якщо ви спробуєте відкрити файл, і він не вдається, бібліотека Java повідомляє вам, що викинувши виняток, тому що API, що призводять до відкриття файлу, не мають іншого механізму повернення помилки.
JeremyP

4
@Coder Залежить. Я думаю, що при виконанні IO або при аналізі складних даних, які майже завжди правильно відформатовані, викид винятку для позначення помилки робить код набагато чистішим.
CodesInChaos

1
@Coded Так, існує метод для того, щоб сказати вам, що він не зміг зробити те, про що просили. Але винятки не слід використовувати для контролю потоку, тому C # також має метод int.TryParse, який не кидає.
Енді,

1
@Coder: Це повне сміття. Виняткові речі на одному рівні коду можуть не бути винятковими на іншому, або ви можете, наприклад, подати або додати інформацію про помилки чи налагодження, перш ніж продовжувати.
DeadMG

Відповіді:


46

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

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

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


1
+1, хороші бали. Я думаю, що деяка обробка може бути дійсною в уловці, наприклад, підготовка повідомлення про помилку та реєстрація помилок. Визволення дорогих ресурсів, таких як db-з'єднання та посилання (.NET) COM, може бути додано до блоку Нарешті.
NoChance

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

@MichaelBorgwardt Я думаю, що дизайн API не такий вже й поганий (хоча це може бути і краще). Спроба проаналізувати недійсний рядок - це явно умова помилки, і таким чином саме "широко прийнятий випадок", який ви згадали. Погоджено, метод визначення того, чи є рядок синтаксично правильним числом, стане в нагоді (саме тут може бути краще). Однак змусити всіх викликати цей метод до фактичного розбору неможливо, і нам потрібно впоратися з помилкою. Змішування фактичного результату та коду помилки незручно, тому ви все одно кинете виняток. Але, можливо, виняток із виконання.
хустка

3
+1 за останнє речення.
FrustratedWithFormsDesigner

2
@scarfridge: Я повністю згоден з вами :) Відсутність булевого повернення методу isInteger (String) - це все, що я мав на увазі під "поганим дизайном API"
Майкл Боргвардт

9

Це те, що, як правило, залежить від мови та парадигми.

Більшість моєї роботи виконується на Java (а іноді і на C ++). Тенденція полягає у використанні виключень лише для виняткових умов. У Stack Overflow є деякі питання щодо ефективності винятків винятків у Java , і, як ви бачите, це не зовсім незначно. Існують також інші проблеми, такі як читабельність та ремонтопридатність коду. При правильному використанні винятки можуть делегувати обробку помилок відповідним компонентам.

Однак у Python панує думка, що простіше просити прощення, ніж дозвіл. У спільноті Python це відоме як EAFP , що протиставляється підходу "дивись перед тим, як стрибнути" ( LBYL ) на мовах С-стилю.


2
+1 за згадування накладних витрат за винятками. Я.
NoChance

2
@EmmadKareem Тільки якщо у вас встановлений налагоджувач. Ви можете кидати кілька тисяч з них за секунду без налагоджувача.
CodesInChaos

@CodeInChaos, ти прав. Як я можу знати, я пишу такий чистий код, який ніколи не отримується під час виконання :)
NoChance

@EmmadKareem Залежно від того, як пише мережева програма, несправності підключення чи протоколу обробляються за винятком. Винятки повинні бути досить швидкими в такому додатку, щоб уникнути тривіальних DoS-атак.
CodesInChaos

@CodeInChaos, це добре знати. Я жартував у своєму останньому коментарі.
NoChance

9

Це не найкращий спосіб думати про блоки "пробувати / ловити". Спосіб замислитись про блоки спробувати / ловити такий: стався збій, де найкраще впоратися з ЦІЙ специфічною невдачею. Це може бути наступний рядок коду, це може бути на двадцять рівнів вгору по ланцюгу викликів. Де б вона не була, там і має бути.

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

Важлива частина, яку потрібно зрозуміти, це: catch block == Я знаю, як з цим боротися.


6

Блоки лову слід використовувати лише для обробки винятків, нічого іншого і ніколи для контролю потоку. У будь-якій ситуації, коли ви хочете використовувати винятки для управління потоком, вам краще перевірити попередні умови.

Поводження з потоком за винятками повільне та семантично неправильне.


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

Чи немає конкретних вказівок, коли писати логіку в блок лову, а коли не робити?
ХашимР

4
Аналізатори чисел Java та текстові формати - відомі приклади використання проблемних винятків: єдиний розумний спосіб перевірити наявність передумови (що рядок являє собою розбірливе число) - спробувати розібрати його, і якщо він не вдається за винятком, який ваш вибір на практиці? Ви повинні зловити виняток і керувати потоком з ним, незалежно від того, що це вважається семантично неправильним.
Joonas Pulakaka

@JoonasPulakka Я думаю, що ваш коментар wrt Кількість парсерів та текстових форматів кваліфікується як повнорозмірна відповідь на це питання
gnat

5

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

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

Код найлегше зрозуміти, коли дії йдуть у простій послідовності, без умов. Винятки покращують ремонтопридатність, видаляючи перевірку помилок із нормального потоку. Не має значення, якщо виконання відповідає нормальному потоку 99,9% часу або 50% часу або 20% часу.

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

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


1

Блоки лову не повинні використовуватися для написання логіки коду. Їх слід використовувати лише для обробки помилок. Прикладом є (1) очищення будь-яких виділених ресурсів, (2) друк корисного повідомлення та (3) витончено вихід.

Це правда, що винятки можна зловживати і викликати небажані gotoнаслідки. Але це не робить їх марними. При правильному використанні вони можуть покращити багато аспектів вашого коду.

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