我没有问题;我只是好奇。想象一下以下场景:
foreach (var foo in list)
{
try
{
//Some code
}
catch (Exception)
{
//Some more code
}
finally
{
continue;
}
}
这不会编译,因为它会引发compiler error CS0157:
控制不能离开finally子句的主体
为什么?
答案 0 :(得分:149)
finally
块都会运行。如果抛出异常,continue
会做什么?您无法继续执行循环,因为未捕获的异常会将控制转移到另一个函数。
即使没有抛出异常,finally
也会在try / catch块中的其他控制转移语句运行时运行,例如return
,这会带来同样的问题。
简而言之,对于finally
的语义,允许将控制从finally
块内部转移到其外部是没有意义的。
使用一些替代语义来支持这一点会更有困难而不是有用,因为有一些简单的解决方法可以使预期的行为更加清晰。所以你得到一个错误,并被迫正确思考你的问题。这是C#中持续存在的“让你陷入成功之中”的想法。
如果你想忽略异常(通常不是一个坏主意)并继续执行循环,请使用catch all block:
foreach ( var in list )
{
try{
//some code
}catch{
continue;
}
}
如果您希望continue
仅在未引发未捕获的异常时,请将continue
放在try-block之外。
答案 1 :(得分:32)
这是一个可靠的来源:
continue语句不能退出finally块(第8.10节)。什么时候 一个continue语句出现在finally块中,即target的目标 continue语句必须在同一个finally块中;否则,a 发生编译时错误。
摘自MSDN, 8.9.2 The continue statement 。
文件说:
当控制离开时,总是执行finally块的语句 试试声明。无论控制转移是否发生,都是如此 正常执行的结果,由于执行休息, 继续,转到或返回语句,或者作为传播的结果 try语句中的异常。如果在此期间抛出异常 执行finally块,异常传播到下一个 附上试试声明。如果另一个例外正在进行中 正在传播,这个例外就会丢失。传播过程 在throw语句的描述中进一步讨论了一个例外(第8.9.5节)。
答案 2 :(得分:31)
你可能认为这是有道理的,但实际上没有意义。
foreach (var v in List)
{
try
{
//Some code
}
catch (Exception)
{
//Some more code
break; or return;
}
finally
{
continue;
}
}
如果抛出异常,您打算做什么中断或继续? C#编译器团队不希望通过假设break
或continue
来自行决策。相反,他们决定抱怨开发人员的情况不明确,从finally block
转移控制权。
因此,开发人员的工作是明确说明他打算做什么,而不是编译器假设其他事情。
我希望你理解为什么这不能编译!
答案 3 :(得分:16)
正如其他人所说,但专注于例外,它实际上是关于转移控制的模糊处理。
在你的心目中,你可能会想到这样的场景:
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;
}
理论上,你可以跟踪控制流并说,是的,这是“好的”。抛出没有异常,没有转移控制。但是C#语言设计师还有其他问题。
public static void Exception()
{
try
{
foreach(var item in list)
{
try
{
throw new Exception("What now?");
}
finally
{
continue;
}
}
}
catch
{
//do I get hit?
}
}
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
仅用于 。
答案 4 :(得分:11)
通常continue
在finally
块中使用时没有意义。看看这个:
foreach (var item in list)
{
try
{
throw new Exception();
}
finally{
//doesn't make sense as we are after exception
continue;
}
}
答案 5 :(得分:5)
“这不会编译,我认为这是完全合理的”
嗯,我认为不是。
如果你真的有catch(Exception)
,那么你不需要最终(甚至可能不是continue
)。
当你有更现实的catch(SomeException)
时,如果未捕获到异常会发生什么?你的continue
想要单行,异常处理另一个。
答案 6 :(得分:3)
你不能离开最后一个街区的尸体。这包括中断,返回和在您的情况下继续关键字。
答案 7 :(得分:3)
finally
块可以在等待重新抛出的异常时执行。能够在不重新抛出异常的情况下退出块(通过continue
或其他任何东西)是没有意义的。
如果你想继续你的循环,你不需要finally语句:只需捕获异常而不要重新抛出。
答案 8 :(得分:1)
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
也会在第三种情况下执行,其中没有任何异常抛出,被捕获或未被捕获。如果需要,您当然可以在try-catch块之后放置一个continue
,而不是在每个catch
内放置。{/ p>
答案 9 :(得分:1)
从技术上讲,它是底层CIL的限制。来自language spec:
除非通过异常处理机制,否则永远不允许控制传输进入catch处理程序或finally子句。
和
只允许通过异常指令(
leave
,end.filter
,end.catch
或end.finally
)来控制从受保护区域转出
在br
instruction的文档页面上:
此指令无法执行控制传入和传出try,catch,filter和finally块。
这最后适用于所有分支指令,包括beq
,brfalse
等。
答案 10 :(得分:-1)
该语言的设计者根本不想(或不能)推断最终块的语义被控制传输终止。
一个问题,或者可能是关键问题,是finally
块作为某些非本地控制传输(异常处理)的一部分执行。控制转移的目标不是封闭循环;异常处理中止循环并继续展开。
如果我们从finally
清理块中进行控制转移,则原始控制转移被“劫持”。它被取消,控制权转移到其他地方。
语义可以解决。其他语言也有。
C#的设计者决定简单地禁止静态的,“类似goto”的控件传输,从而在某种程度上简化了事情。
但是,即使你这样做,它也无法解决如果从finally
启动动态传输会发生什么的问题:如果finally块调用一个函数,该函数抛出怎么办?然后,原始异常处理被“劫持”。
如果你计算出第二种劫持形式的语义,就没有理由放弃第一种类型的劫持。它们实际上是一回事:控制转移是一种控制转移,无论它是否是相同的词汇范围。