Какой смысл, наконец, в try catch/ кроме оператора finally
В течение многих лет я использовал варианты try-catch/exception-finally во многих языках, сегодня кто-то спросил меня, в чем смысл, наконец, и я не смог ответить.
По сути, зачем вам ставить оператор в конце вместо того, чтобы помещать его после всего блока try-catch? Или, другими словами, есть ли разница между следующими блоками кода:
try{ //a}
catch {//b}
finally {//c}
try{//a}
catch{//b}
//c
РЕДАКТИРОВАТЬ:
ЛЮДИ, я знаю, что в конце концов делает, я использую это целую вечность, но мой вопрос в приведенном выше примере //c
в конце концов кажется излишним, не так ли?
6 ответов
Цель finally
Блок должен гарантировать, что код запускается в трех обстоятельствах, которые не очень аккуратно обрабатываются с использованием одних блоков "catch":
- Если код внутри блока `try` выходит через` return`
- Если код в блоке catch либо перебрасывает перехваченное исключение, либо - случайно или преднамеренно - в результате выдает новое.
- Если код внутри блока `try` встречает исключение, для которого нет перехвата.
Можно было бы скопировать finally
код перед каждым return
или бросить, и завернуть catch
блоки в пределах их собственного try / catch, чтобы учесть возможность возникновения случайного исключения, но гораздо проще отказаться от всего этого и просто использовать finally
блок.
Кстати, одна вещь, которую я хотел бы включить в дизайнеры языков - это exception
аргумент finally
блок, чтобы иметь дело со случаем, когда нужно очистить после исключения, но все еще хочет, чтобы он просочился вверх по стеку вызовов (например, можно обернуть код для конструктора в такую конструкцию, и Dispose
строящийся объект, если конструктор собирался выйти с исключением).
Чтобы было еще проще понять:
try{//a}
catch{//b}
//c
В приведенном выше коде //c не выполнится:
- если вы используете «return» внутри блока try. **
- если вы используете «возврат» внутри блока catch. **
- если вы вызовете какое-либо исключение внутри блока catch.
- если ваш блок try вызывает исключение, которое не может быть перехвачено вашим блоком catch.
В приведенном ниже коде:
try{ //a}
catch {//b}
finally {//c}
//c выполнится несмотря ни на что.
Блок finally выполняется, даже если исключение выдается в блоке try. Поэтому, например, если вы открывали поток раньше, вы можете захотеть закрыть этот поток, либо сгенерировано исключение, либо нет. Наконец, блок полезен для такой проблемы.
finally
это синтаксический сахар, позволяющий использовать принцип DRY в
try-catch
шаблон. Исключение обычно генерируется, если код библиотеки не имеет достаточно информации для обработки некоторого состояния и хочет, чтобы код клиента разрешил его. Если у вас нет разделения кода библиотеки и клиента, вы можете справиться со всем с помощью
if
вместо
try
.
Посмотрим на стандартную ситуацию без
finally
:
void myFunction() {
var r = allocateResources();
r.doSomething();
if(somethingBadHappens) {
freeResources(r);
throw new Exception(CODE42);
}
r.doSomethingMore();
freeResources(r);
}
В приведенном выше фрагменте вы повторяете
freeResources()
: это может быть несколько утверждений, которые необходимо повторить. Это пахнет и
finally
block - это решение для чистого кода:
void myFunction() {
var r = allocateResources();
try {
r.doSomething();
if(somethingBadHappens) throw new Exception(CODE42);
r.doSomethingMore();
}
finally {
freeResources(r);
}
happyFunction();
}
Реализуем три уровня абстракции:
- A1 - это код библиотеки, обеспечивающий
allocateResources()
функция - A2 - это наш код, обеспечивающий
myFunction
, потребляя A1 - A3 - это некоторый клиентский код, потребляющий
myFunction
в блоке try-catch:
function A3code() {
try {
myFunction();
doSomething();
}
catch(Exception e) {
// no hanging resources here
Console.WriteLine(e);
}
}
Теперь посмотрим, что может случиться:
- если
allocateResources()
бросает в A1, мы не знаем, как обработать это в A2 (код A2 можно запускать в среде без консоли), поэтому мы делагируем ситуацию до A3, не добавляя никакого дополнительного кода. Если здесь выбрасывается Exception, блок finally не выполняется, посколькуfinally
связан сtry
который не был введен. - если
somethingBadHappens
в блоке try стек раскручивается до A3, где ситуация обрабатывается, НО перед этимfinally
блок выполняется, поэтому нам не нужно его повторять, если не происходит исключений. - до
finally
мы можем добавитьcatch
заблокировать и попытаться разрешить некоторые потенциальные исключения из A1, которые могут появиться при вызовеr.doSomething
методы. Обычно мы хотим обрабатывать исключения как можно скорее, чтобы сделать клиентский код (A3) более удобным для клиентских программистов. happyFunction()
выполняется, только если ничего не вбрасываетсяmyFunction()
(внутри или снаружиtry
блок).
Как указывает @supercat,
finally
блок выполняется также, если
try
блок выходит через возврат. Я предлагаю вам избежать этой дурной привычки и иметь только один возврат в каждой функции (возможно, некоторые из них существуют в самом начале функций). Причины использования функций с однократным возвратом:
- Код более читабелен: вы смотрите в конец и видите, ЧТО возвращает функция. При многократном возврате вы должны найти все случаи возврата, проверить все "если", подумать о том, когда "если" будут выполнены, и только тогда вы узнаете, что возвращает функция.
- Код может быть оптимизирован компиляторами, см. Copy elision.
Причина множественных возвратов заключается в том, чтобы избежать множества вложенных if, но есть и другие методы решения этой проблемы.
Exception
s являются исключением из этого правила.
Finally
убедитесь, что ваш код выполняется, даже если вы получили исключение.
Блок finally полезен для очистки любых ресурсов, выделенных в блоке try, а также для запуска любого кода, который должен выполняться, даже если есть исключение.
http://msdn.microsoft.com/en-us/library/zwc8s4fz(v=vs.80).aspx
Учитесь на примере
let v = 0;
function f() {
try {
v = 1;
return 2;
} finally {
v = 3;
return 4;
}
v = 5;
return 6;
}
const r = f();
console.log(r, v);
следующие отпечатки "3, 4"