Почему оператор "продолжить" не может находиться внутри блока "finally"?
У меня нет проблем; Мне просто интересно. Представьте себе следующий сценарий:
foreach (var foo in list)
{
try
{
//Some code
}
catch (Exception)
{
//Some more code
}
finally
{
continue;
}
}
Это не будет компилироваться, так как он вызывает ошибку компилятора CS0157:
Элемент управления не может покинуть тело предложения finally
Почему?
Ответы
Ответ 1
finally
блоки запускают ли исключение или нет. Если вы выбрали исключение, что бы сделать continue
? Вы не можете продолжить выполнение цикла, потому что неперехваченное исключение передаст управление другой функции.
Даже если исключение не выбрано, finally
будет выполняться, когда выполняются другие операторы передачи управления внутри блока try/catch, например, return
, что приводит к той же проблеме.
Короче говоря, с семантикой finally
нет смысла разрешать перенос элемента управления изнутри блока finally
на внешнюю сторону.
Поддержка этого с помощью некоторой альтернативной семантики будет более запутанной, чем полезной, поскольку существуют простые обходные пути, которые делают путь предполагаемого поведения более понятным. Таким образом, вы получаете ошибку и вынуждены правильно думать о своей проблеме. Это общая идея "бросьте в яму успеха", которая продолжается в С#.
![C#, you, and the out if success]()
Если вы хотите игнорировать исключения (чаще всего это плохая идея) и продолжать выполнение цикла, используйте catch весь блок:
foreach ( var in list )
{
try{
//some code
}catch{
continue;
}
}
Если вы хотите continue
только тогда, когда не выбраны неперехваченные исключения, просто поставьте continue
вне блока try.
Ответ 2
Вот надежный источник:
Оператор continue не может выйти из блока finally (раздел 8.10). когда оператор continue появляется в блоке finally, цель оператор continue должен находиться в одном и том же окончательном блоке; в противном случае ошибка времени компиляции.
Это взято из MSDN, 8.9.2 Инструкция continue.
В документации указано, что:
Операторы блока finally всегда выполняются, когда управление оставляет попробуйте. Это верно, если передача управления происходит как результат нормального выполнения, в результате выполнения перерыва, continue, goto или return, или в результате распространения исключение из инструкции try. Если во время выполнение блока finally, исключение распространяется на следующий прилагая инструкцию try. Если другое исключение находилось в процессе что это исключение теряется. Процесс распространения исключение обсуждается далее в описании оператора throw (раздел 8.9.5).
Это отсюда 8.10 Инструкция try.
Ответ 3
Вы можете подумать, что это имеет смысл, но не имеет смысла.
foreach (var v in List)
{
try
{
//Some code
}
catch (Exception)
{
//Some more code
break; or return;
}
finally
{
continue;
}
}
Что вы намерены сделать break или продолжить при вызове исключения? Команда компилятора С# не хочет принимать решение самостоятельно, предполагая break
или continue
. Вместо этого они решили жаловаться, что ситуация с разработчиком будет неоднозначной для передачи управления с finally block
.
Таким образом, разработчик должен четко указать, что он намерен делать, а не компилятор, предполагающий что-то еще.
Надеюсь, вы понимаете, почему это не скомпилируется!
Ответ 4
Как утверждали другие, но сфокусированы на исключениях, это действительно касается двусмысленной обработки передаваемого управления.
В вашем уме вы, вероятно, думаете о таком сценарии:
public static object SafeMethod()
{
foreach(var item in list)
{
try
{
try
{
//do something that won't transfer control outside
}
catch
{
//catch everything to not throw exceptions
}
}
finally
{
if (someCondition)
//no exception will be thrown,
//so theoretically this could work
continue;
}
}
return someValue;
}
Теоретически вы можете отслеживать поток управления и говорить "да", это "нормально". Никакое исключение не выбрасывается, управление не передается. Но разработчики языка С# имели другие проблемы.
Выброшенное исключение
public static void Exception()
{
try
{
foreach(var item in list)
{
try
{
throw new Exception("What now?");
}
finally
{
continue;
}
}
}
catch
{
//do I get hit?
}
}
The Dreaded Goto
public static void Goto()
{
foreach(var item in list)
{
try
{
goto pigsfly;
}
finally
{
continue;
}
}
pigsfly:
}
Возврат
public static object ReturnSomething()
{
foreach(var item in list)
{
try
{
return item;
}
finally
{
continue;
}
}
}
Разрыв
public static void Break()
{
foreach(var item in list)
{
try
{
break;
}
finally
{
continue;
}
}
}
Итак, в заключение, да, хотя есть небольшая возможность использовать continue
в ситуациях, когда контроль не передается, но много (большинство?) случаев связаны с исключениями или блоками return
. Разработчики языка чувствовали, что это слишком неоднозначно и (вероятно) невозможно гарантировать во время компиляции, что ваш continue
используется только в тех случаях, когда поток управления не передается.
Ответ 5
В общем случае continue
не имеет смысла при использовании в блоке finally
. Взгляните на это:
foreach (var item in list)
{
try
{
throw new Exception();
}
finally{
//doesn't make sense as we are after exception
continue;
}
}
Ответ 6
"Это не скомпилируется, и я думаю, что это имеет смысл"
Ну, я думаю, что нет.
Когда вы буквально имеете catch(Exception)
, тогда вам не понадобится окончательно (и, возможно, даже не continue
).
Когда у вас есть более реалистичный catch(SomeException)
, что должно произойти, если исключение не поймано? Ваш continue
хочет идти в одну сторону, а обработка исключений - другая.
Ответ 7
Вы не можете оставить тело блока finally. Это включает в себя разрыв, возврат и в вашем случае продолжение ключевых слов.
Ответ 8
Блок finally
может быть выполнен с исключением, ожидающим возврата. Было бы бесполезно выйти из блока (через continue
или что-нибудь еще), не переустанавливая исключение.
Если вы хотите продолжить цикл, что бы ни случилось, вам не нужен оператор finally: просто поймайте исключение и не переверните.
Ответ 9
finally
запускается независимо от того, выбрано ли неперехваченное исключение. Другие уже объяснили, почему это делает continue
нелогичным, но вот альтернатива, которая следует духу того, что, по-видимому, требует этот код. В основном, finally { continue; }
говорит:
- Если есть исключения, продолжите
- Когда есть неотображаемые исключения, разрешите их бросать, но продолжайте
(1) может быть выполнено путем размещения continue
в конце каждого catch
, и (2) может быть выполнено путем хранения неперехваченных исключений, которые будут выбраны позже. Вы можете написать это следующим образом:
var exceptions = new List<Exception>();
foreach (var foo in list) {
try {
// some code
} catch (InvalidOperationException ex) {
// handle specific exception
continue;
} catch (Exception ex) {
exceptions.Add(ex);
continue;
}
// some more code
}
if (exceptions.Any()) {
throw new AggregateException(exceptions);
}
Собственно, finally
также выполнил бы в третьем случае, где не было исключений, которые вообще были бы выбраны, пойманы или не пойманы. Если бы это было необходимо, вы могли бы просто разместить один continue
после блока try-catch вместо каждого catch
.
Ответ 10
Технически говоря, это ограничение базового CIL. Из спецификация языка:
Передача управления никогда не разрешается вводить обработчик catch или finally, кроме как через механизм обработки исключений.
и
Передача управления из защищенной области разрешена только с помощью команды исключения (leave
, end.filter
, end.catch
или end.finally
)
На странице doc для br
инструкция:
Управление передачей в и из блоков try, catch, filter и finally не может выполняться этой инструкцией.
Последнее значение сохраняется для всех команд ветвления, включая beq
, brfalse
и т.д.
Ответ 11
Поскольку Python 3.8
continue
в блоке finally
разрешен.
Заявление продолжения было незаконным в предложении finally из-за проблема с реализацией. В Python 3.8 это ограничение было поднял. (Внесено Сергеем Сторчака в bpo-32489.)
https://docs.python.org/3.8/whatsnew/3.8.html
Ответ 12
Дизайнеры языка просто не хотели (или не могли) рассуждать о том, что семантика окончательного блока заканчивается передачей управления.
Одна проблема, или, возможно, ключевая проблема, заключается в том, что блок finally
запускается как часть некоторой нелокальной передачи управления (обработка исключений). Целью этой передачи управления не является замкнутый цикл; обработка исключений прерывает цикл и продолжает раскручивать дальше.
Если у нас есть передача управления из блока очистки finally
, тогда первоначальная передача управления будет "захвачена". Он отменяется, и контроль идет в другом месте.
Семантика может быть разработана. На других языках есть.
Дизайнеры С# решили просто запретить статические, "переходные" передачи управления, тем самым несколько упростив некоторые вещи.
Однако, даже если вы это сделаете, он не решает вопрос о том, что произойдет, если динамическая передача инициируется с помощью finally
: что, если блок finally вызывает функцию, а эта функция выбрасывает? Исходная обработка исключений затем "захвачена".
Если вы выработаете семантику этой второй формы угона, нет причин изгнать первый тип. Они на самом деле одно и то же: передача управления - это передача управления, независимо от того, является ли она той же лексической областью или нет.