В чем смысл блока finally?

Помимо синтаксиса, в чем разница между

try {
}
catch() {
}
finally {
    x = 3;
}

и

try {
}
catch() {
}

x = 3;

изменить: в .NET 2.0?


так

try {
    throw something maybe
    x = 3
}
catch (...) {
    x = 3
}

поведенчески эквивалентно?

Вы можете указать язык программирования. Поведение будет зависеть от языка.

Eric Z Beard 09.09.2008 00:44

Чтобы выбрать лучший ответ, я думаю, вам нужно уточнить, действительно ли блоки try / catch пусты или нет. Как указано в одном из ответов, семантика одинакова, если блоки try / catch пусты, в противном случае разные ответы касаются семантических различий.

Kasper 09.09.2008 00:55

Я согласен с предыдущими комментариями, вам необходимо уточнить реальный язык и серьезно изучить полученные ответы. Обновленные ответы либо неверны, либо неполны.

Rob Cooper 09.09.2008 01:21
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
33
3
5 000
16
Перейти к ответу Данный вопрос помечен как решенный

Ответы 16

Таким образом, вы можете очистить любые открытые соединения и т. д., Инициализированные в блоке try. Если вы открыли соединение, а затем возникло исключение, это исключение не будет закрыто должным образом. Для этого типа сценария и нужен блок finally.

В Java:

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

Всегда очень сильное слово

mattumotu 29.10.2018 12:56
Ответ принят как подходящий

Зависит от языка, поскольку могут быть небольшие семантические различия, но идея состоит в том, что он будет выполняться (почти) всегда, даже если код в блоке try выдал исключение.

Во втором примере, если код в блоке catch возвращается или завершается, x = 3 не будет выполняться. Во первых так и будет.

В платформе .NET в некоторых случаях выполнение блока finally не происходит: Исключения безопасности, приостановка потоков, выключение компьютера :) и т. д.

Я бы добавил к списку методов, предотвращающих выполнение блока finally, вызов Environment.Exit.

Luca 20.11.2010 01:12

Ну, во-первых, если вы вернетесь в свой блок try, finally все равно будет работать, но код, указанный под блоком try-catch-finally, не будет.

Ты прав. Это хороший ответ, но я полагаю, что он не дает прямого ответа на поставленный вопрос. Может быть, это означает, что мне следовало задать вопрос получше :)

Keshi 09.09.2008 01:52

Здесь все правы. Полагаю, я был сокращен. Я заметил, что первые ответы получают все положительные голоса. ;-)

Josh Hinman 09.09.2008 02:55

Это было мое подозрение, и я ценю подтверждение. Хотя он краток, он дает правильный ответ.

Paul Russell 03.11.2017 18:58

Блок finally должен выполняться независимо от того, поймали ли вы исключение или нет. См. Попробуйте / Поймайте / Наконец пример

В случае, если try и catch пусты, разницы нет. В противном случае вы можете быть уверены, что окончательный вариант будет выполнен.

Если вы, например, создадите новое исключение в своем блоке catch (повторно выбросите), то присвоение будет выполнено только в том случае, если оно находится в блоке finally.

Обычно finally используется для очистки после себя (закрытие соединений с БД, дескрипторов файлов и т.п.).

Вы никогда не должны использовать управляющие операторы (return, break, continue) в finally, так как это может быть кошмаром обслуживания и поэтому считается плохой практикой.

Блок finally всегда будет вызываться (ну, на самом деле не всегда ...), даже если генерируется исключение или достигается оператор возврата (хотя это может зависеть от языка). Это способ очистки, который, как вы знаете, всегда будет называться.

Мне всегда нравилась ежедневная статья о wtf, на которую вы ссылались, - особенно один из комментариев, в котором говорится, что нам нужна magicFairyFinally, которая гарантированно работает даже без питания!

Jarrod Dixon 09.09.2008 01:00

@Ed, возможно, вы думаете о чем-то вроде catch(...), который перехватывает неуказанное исключение в C++.

Но finally - это код, который будет выполняться независимо от того, что происходит в блоках catch.

У Microsoft есть справочная страница на наконец-то попробуйте для C#

Блок finally находится в той же области видимости, что и try / catch, поэтому у вас будет доступ ко всем переменным, определенным внутри.

Представьте, что у вас есть обработчик файлов, в этом разница, как он будет написан.

try
{
   StreamReader stream = new StreamReader("foo.bar");
   stream.write("foo");
}
catch(Exception e) { } // ignore for now
finally
{
   stream.close();
}

в сравнении с

StreamReader stream = null;
try
{
    stream = new StreamReader("foo.bar");
    stream.write("foo");
} catch(Exception e) {} // ignore

if (stream != null)
    stream.close();

Однако помните, что что-либо внутри, наконец, не может работать. Представьте, что вы получили сигнал прерывания, вылетели окна или пропало электричество. В конечном итоге полагаться на критически важный для бизнеса код - это плохо.

пожалуйста, определите язык, поскольку это подразумевается в вашем ответе, но не указано.

shsteimer 09.09.2008 02:22

Переполнение стека также приведет к тому, что блок finally не будет выполняться.

David Basarab 10.09.2008 19:41

Я думаю, что первый пример кода неверен (в C#) - переменные, объявленные в блоке try, имеют область видимости нет в блоках catch или finally. Я попробовал это в тестовом приложении C# и получил ошибку времени компиляции в ссылке на блок finally: «Имя 'stream' не существует в текущем контексте».

Jon Schneider 18.09.2008 21:35

Любой код в finally запускается даже в случае необработанного исключения. Обычно код finally используется для очистки локальных объявлений неуправляемого кода с помощью .dispose ().

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

Если вы попробуете этот пример:

try {
  return 0;
} finally {
  return 2;
}

Результат будет 2 :)

Сравнение с другими языками: Вернуться из наконец

Имейте в виду, что блок Final не всегда выполняется. Переполнение стека (не каламбур) приведет к тому, что блок finally не будет выполняться.

David Basarab 10.09.2008 19:40

Результат будет 2 на Java. В C# этот код нельзя скомпилировать.

thorn̈ 06.03.2010 15:58

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

@mats очень верен в том, что всегда есть вероятность "серьезных" сбоев - блоки finally не должны включать критически важный код, который всегда должен выполняться транзакционно внутри попытки {}

@mats снова - настоящая красота в том, что он позволяет вам отбрасывать исключения из ваших собственных методов и при этом гарантирует, что вы уберете:

try
{
StreamReader stream = new StreamReader("foo.bar");
mySendSomethingToStream(stream);
}
catch(noSomethingToSendException e) {
    //Swallow this    
    logger.error(e.getMessage());
}
catch(anotherTypeOfException e) {
    //More serious, throw this one back
    throw(e);
}
finally
{
stream.close();
}

Таким образом, мы можем перехватывать многие типы исключений, обрабатывать их по-разному (первый допускает выполнение чего-либо, кроме try {}, второй эффективно возвращает), но всегда аккуратно и аккуратно проясняется.

Есть несколько вещей, которые делают блок finally полезным:

  1. Если вы вернетесь из блоков try или catch, блок finally все еще будет выполняться, прямо перед тем, как управление будет возвращено вызывающей функции.
  2. Если в блоке catch возникает исключение или в блоке try возникает исключение неперехваченного типа, код в блоке finally все равно выполняется.

Эти блоки make finally отлично подходят для закрытия файловых дескрипторов или сокетов.

@iAn и @mats:

Я бы не стал "срывать" что-либо в finally {}, что было "настроено" в try {}, как правило. Было бы лучше вытащить создание потока вне try {}. Если вам нужно обработать исключение при создании потока, это можно сделать в большем объеме.

StreamReader stream = new StreamReader("foo.bar");  
try {
    mySendSomethingToStream(stream);
}
catch(noSomethingToSendException e) {
    //Swallow this    
    logger.error(e.getMessage());
}
catch(anotherTypeOfException e) {
    //More serious, throw this one back
    throw(e);
}
finally {
    stream.close();  
}

Обратите внимание: линия throw(e); //rewrites stack trace отличается от throw; //re-throws the original exception.

vitule 24.03.2010 18:27

В Java вы используете его для всего, что хотите выполнить, независимо от того, использовали ли вы «возврат», просто выполнили блок try или было перехвачено исключение.

Например, закрытие сеанса базы данных или JMS-соединения или освобождение некоторого ресурса ОС.

Я предполагаю, что это похоже на .NET?

Это вопрос Старый, но меня это всегда беспокоило (не зря я нашел его здесь). Я прочитал все ответы, и мне кажется, что никто не продумал их до конца.

Это не ответ. Я действительно думаю, что в finally нет ничего хорошего, возможно, поэтому он не существовал в языках программирования до «недавнего времени». Большинство примеров, в которых указывается, что stream.close() может вызывать исключения нулевой ссылки, поэтому вам все равно придется проверить, является ли он нулевым.

Да, если вы вернетесь из try{}, finally все равно будет работать. Но разве это хорошая практика? Похоже на гинастику, с таким же успехом можно было бы вернуть goto. Почему бы не подождать и не вернуться после блока? Все, что делает finally {}, - это добавляет в ваш код две или три строки.

Другие вопросы по теме