Эффективное использование блока try / catch?

21

Должны ли блоки catch использоваться для написания логики, например, для управления потоком и т. Д.? Или просто для того, чтобы бросать исключения? Влияет ли это на эффективность или удобство сопровождения кода?

Каковы побочные эффекты (если они есть) написания логики в блоке catch?

РЕДАКТИРОВАТЬ:

Я видел класс Java SDK, в котором они написали логику внутри блока catch. Например (фрагмент взят из 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 :

Я проходил урок, где они считают это преимуществом написания логики исключительных случаев внутри исключений:

Исключения позволяют вам написать основной поток вашего кода и иметь дело с исключительными случаями в другом месте.

Какие-то конкретные рекомендации, когда писать логику в блоке catch, а когда нет?

HashimR
источник
12
@ Кодер, я думаю, ты немного переоценен. Тем более, что со многими из существующих API вы не можете избежать этого.
CodesInChaos
8
@Coder: В Java исключения часто используются в качестве механизма сигнализации о проблемах, являющихся частью законного потока кода. Например, если вы попытаетесь открыть файл, но он не удастся, библиотека Java сообщит вам об этом, вызвав исключение, потому что API, которые приводят к открытию файла, не имеют другого механизма возврата ошибки.
JeremyP
4
@ Кодер Зависит. Я думаю, что при выполнении ввода-вывода или при анализе сложных данных, которые почти всегда правильно отформатированы, создание исключения, означающего ошибку, делает код намного чище.
CodesInChaos
1
@Coded Да, существует метод, позволяющий вам сказать, что он не смог сделать то, о чем его просили. Но исключения не должны использоваться для управления потоком, поэтому в C # также есть метод int.TryParse, который не выдает.
Энди
1
@ Кодер: Это полная чушь. Вещи, которые являются исключительными для одного уровня кода, могут не быть исключительными для другого - или вы можете, например, представить или добавить информацию об ошибке или отладочную информацию, прежде чем продолжить.
DeadMG

Ответы:

46

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

На техническом уровне throw и try / catch являются конструкциями потока управления, которые позволяют вам перепрыгивать через стек вызовов, ни больше, ни меньше. Прыжок вверх по стеку вызовов неявно соединяет код, который не находится близко друг к другу в источнике, что плохо для удобства сопровождения . Поэтому его следует использовать только тогда, когда вам нужно это сделать, а альтернативы еще хуже. Широко признается случай , когда альтернативы хуже обработки ошибок (специальные коды возврата , которые должны быть проверены и прошли вверх каждый уровень стека вызовов вручную).

Если у вас есть случай, когда альтернативы хуже (и вы действительно тщательно их рассмотрели), то я бы сказал, что использование throw и try / catch для управления процессом - это нормально. Догма не является хорошей заменой суждению.

Майкл Боргвардт
источник
1
+1, хорошие очки сделаны. Я думаю, что некоторая обработка может быть действительной в улове, такой как подготовка сообщения об ошибке и регистрация ошибок. Освобождение дорогих ресурсов, таких как соединения с БД и COM-ссылки (.NET), можно добавить в блок «Наконец».
NoChance
@EmmadKareem Я думал о том, чтобы освободить ресурсы, но обычно в любом случае их нужно освобождать в любой момент, даже без ошибок. Таким образом, вы можете повторить себя. Подготовка сообщения журнала, конечно, приемлемо.
шарфридж
@MichaelBorgwardt Я думаю, что дизайн API не так уж и плох (хотя может быть и лучше). Попытка разобрать недопустимую строку явно является ошибочным условием и, таким образом, является именно тем «широко принятым случаем», который вы упомянули. Согласен, метод, чтобы определить, является ли строка синтаксически правильным числом, пригодится (это, где это могло бы быть лучше). Однако заставить всех вызвать этот метод перед фактическим анализом невозможно, и нам нужно обработать ошибку. Смешивать фактический результат и код ошибки неудобно, и вы все равно выбросите исключение. Но, возможно, исключение во время выполнения.
шарфридж
3
+1 за последнее предложение.
FrustratedWithFormsDesigner
2
@scarfridge: я полностью с тобой согласен :) Отсутствие логически возвращаемого метода isInteger (String) - это все, что я имел в виду под «плохим дизайном API»
Майкл Боргвардт,
9

Это то, что имеет тенденцию зависеть от языка и парадигмы.

Большая часть моей работы выполняется на Java (а иногда и на C ++). Тенденция заключается в использовании исключений только для исключительных условий. В отношении переполнения стека есть несколько вопросов о накладных расходах исключений в Java , и, как вы можете видеть, это не совсем незначительно. Есть и другие проблемы, такие как читаемость и удобство сопровождения кода. При правильном использовании исключения могут делегировать обработку ошибок соответствующим компонентам.

Тем не менее, в Python, идея, что проще просить прощения, чем разрешение правит. В сообществе Python это известно как EAFP , что контрастирует с подходом «посмотри перед прыжком» ( LBYL ) в языках стиля C.

Томас Оуэнс
источник
2
+1 за упоминание накладных расходов с исключениями. Я работаю с .NET и (по крайней мере, на моей машине) я даже чувствую, что должно произойти исключение из-за продолжительности, которая требуется в некоторых случаях по сравнению с другими обычными операциями.
NoChance
2
@EmmadKareem Только если у вас есть отладчик. Вы можете выбросить несколько тысяч из них в секунду без отладчика.
CodesInChaos
@CodeInChaos, вы правы. Откуда мне знать, я пишу такой чистый код, который никогда не получится во время выполнения :)
NoChance
@EmmadKareem В зависимости от того, как пишется сетевое приложение, сбои подключения или протокола обрабатываются с исключением. Исключения должны быть достаточно быстрыми в таком приложении, чтобы избежать тривиальных DoS-атак.
CodesInChaos
@CodeInChaos, это полезно знать. Я шутил в своем последнем комментарии.
NoChance
9

Это не лучший способ думать о блоках try / catch. Способ думать о блоках try / catch заключается в следующем: произошел сбой, где лучше всего справиться с ЭТИМ конкретным отказом. Это может быть следующая строка кода, это может быть двадцать уровней вверх по цепочке вызовов. Где бы это ни было, именно там оно и должно быть.

То, что делает блок catch, зависит от ошибки и того, что вы можете с ней сделать. Иногда его можно просто проигнорировать (неудача в удалении чистого файла без каких-либо важных данных), иногда он будет использоваться для установки значения, возвращаемого функцией, равным true или false (метод int parse java), иногда вы выходите из программы , Все это зависит от ошибки.

Важная часть, чтобы понять это: ловить блок == Я знаю, как с этим бороться.

jmoreno
источник
6

Блоки Catch должны использоваться только для обработки исключений, ничего больше и никогда для потока управления. В любой ситуации, когда вы хотите использовать исключения для управления потоком, лучше проверить предварительные условия.

Обработка потока с исключениями медленная и семантически некорректная.

Том Сквайрс
источник
4
Я знаю , что вы говорите, но стоит отметить , что все , что исключения делают поток программы ручки - только что она должна использоваться только для управления исключительного потока ошибок ...
Макс
Нет ли конкретных указаний, когда писать логику в блоке catch, а когда нет?
HashimR
4
Синтаксические анализаторы чисел и форматеры текста в Java являются известными примерами использования проблемных исключений: единственный разумный способ проверить предварительное условие (что строка представляет анализируемое число) - это попытаться выполнить его синтаксический анализ, и, если произойдет сбой с исключением, каковы ваши варианты выбора? на практике? Вы должны перехватить исключение и управлять им, независимо от того, считается ли оно семантически неверным.
Joonas Pulakka
@JoonasPulakka Я думаю, что ваш комментарий по отношению к анализаторам чисел и форматерам текста квалифицируется как полноразмерный ответ на этот вопрос
gnat
5

Должны ли блоки catch использоваться для написания логики, например, для управления потоком и т. Д.? Или просто для того, чтобы бросать исключения? Влияет ли это на эффективность или удобство сопровождения кода?

Во-первых, забудьте о том, что «исключения должны использоваться для исключительных условий». Также перестаньте беспокоиться об эффективности, пока у вас не будет кода, который работает недопустимо, и вы не знаете, в чем проблема.

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

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

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

Кевин Клайн
источник
1

Блоки Catch не должны использоваться для написания логики кода. Их следует использовать только для обработки ошибок. Примером является (1) очистка любых выделенных ресурсов, (2) печать полезного сообщения и (3) корректный выход.

Это правда, что исключениями можно злоупотреблять и вызывать нежелательные gotoпоследствия. Но это не делает их бесполезными. При правильном использовании они могут улучшить многие аспекты вашего кода.

sakisk
источник