为什么不能在“最后”的块中join“继续”的声明呢?

我没有问题; 我只是好奇。 想象一下下面的场景:

foreach (var foo in list) { try { //Some code } catch (Exception) { //Some more code } finally { continue; } } 

这将不会编译,因为它会引发编译器错误CS0157 :

控制不能离开最后条款的主体

为什么?

finally块运行是否抛出exception。 如果抛出一个exception,那么它会continue做什么? 您不能继续执行循环,因为未捕获的exception会将控制转移到另一个函数。

即使没有抛出任何exception,当try / catch块内的其他控制传输语句运行时, finally也会运行,例如return ,这带来了同样的问题。

简而言之,就finally的语义来说,允许从finally块内部向其外部传递控制是没有意义的。

用一些替代的语义来支持这一点会比有帮助的更混乱,因为有简单的解决方法可以使预期的行为方式更清晰。 所以你得到一个错误,并被迫正确地思考你的问题。 这是C#中将要发生的一切“让你陷入成功之坑”的想法。

C#,你,如果成功的话

如果你想忽略exception(通常不是一个坏主意)并继续执行循环,则使用catch all块:

 foreach ( var in list ) { try{ //some code }catch{ continue; } } 

如果只想在没有捕获到未捕获的exception的情况下continue ,只需在try-block之外continue

这是一个可靠的来源:

continue语句不能退出finally块(8.10节)。 在finally块内发生continue语句时,continue语句的目标必须位于finally块内, 否则,会发生编译时错误。

它来自MSDN, 8.9.2继续声明

该文件说:

当控制离开一个try语句时,finally块的语句总是被执行。 无论是执行中断,继续,转到还是返回语句,还是作为从try语句中传出exception的结果,控制转移都是正常执行的结果,都是如此。 如果在执行finally块期间引发exception,则将exception传播到下一个封闭的try语句。 如果另一个exception正在被传播,那么这个exception就会丢失。 在throw语句的描述中进一步讨论了传播exception的过程(第8.9.5节)。

这是从这里8.10的尝试声明

你可能认为这是有道理的,但实际上并没有意义

 foreach (var v in List) { try { //Some code } catch (Exception) { //Some more code break; or return; } finally { continue; } } 

抛出exception时,你打算如何rest继续 ? C#编译器团队不想通过假设breakcontinue来自行做出决定。 相反,他们决定抱怨开发商的情况将模糊从finally block转移到控制finally block

所以开发者的工作就是清楚地表明他打算做什么,而不是编译器假设其他的东西。

我希望你明白为什么这不编译!

正如其他人所说的,但关注例外情况,其实是关于模糊处理转移控制。

在你的想法中,你可能正在考虑这样一个场景:

 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; } 

理论上,你可以跟踪控制stream程,并说,是的,这是“好”。 没有例外,没有控制转移。 但是C#语言devise人员还有其他的问题。

抛出的exception

 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块。 语言devise者觉得这样做太模棱两可,并且(可能)不可能在编译时确保只有在控制stream程没有被转移的情况下 continue使用。

一般在finally块中continue是没有意义的。 看看这个:

 foreach (var item in list) { try { throw new Exception(); } finally{ //doesn't make sense as we are after exception continue; } } 

“这不会编译,我认为这是完全合理的”

那么,我认为它不。

当你真的有catch(Exception)那么你不需要最后(甚至可能不continue )。

当你有更现实的catch(SomeException) ,当一个exception没有被捕获时会发生什么? 你的continue想去一个方向,exception处理另一个。

你不能离开最后一块的身体。 这包括rest,返回和你的情况下继续关键字。

finally块可以被执行,并等待被重新抛出。 在没有重新抛出exception的情况下能够退出块(通过continue或其他)是没有意义的。

如果你想继续你的循环,不pipe发生什么,你都不需要最后的声明:只要赶上例外,不要再抛出。

finally运行是否抛出一个未捕获的exception。 其他人已经解释了为什么这样做continue不合逻辑的,但是这是一个替代scheme,它遵循这个代码似乎要求的精神。 基本上, finally { continue; } finally { continue; }是说:

  1. 发生exception时,请继续
  2. 当有未捕获的exception时,允许它们被抛出,但仍然继续

(1)可以通过在每次catch结束时放置continue来满足,并且(2)可以通过存储稍后抛出的未捕获的exception来满足。 你可以这样写:

 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

从技术上讲,这是CIL底层的限制。 从语言规范 :

除非通过exception处理机制,否则绝对不允许控制转移进入catch处理程序或finally子句。

只有通过一个例外指令( leaveend.filterend.catchend.finally )才允许控制转出受保护区域,

br指令的doc页面上:

控制传入和传出try,catch,filter,最后块不能被这条指令执行。

所有分支指令都适用,包括beqbrfalse等。

该语言的devise者根本不想(或不能)推断由控制转移终止的finally块的语义。

一个问题,也许是关键问题,是finally块被作为一些非本地控制传输(exception处理)的一部分被执行。 该控制转移的目标不是封闭循环; exception处理将中止循环并继续进一步展开。

如果我们有一个控制转移出finally清理块,那么原来的控制转移就被“劫持”了。 它被取消,控制权转移到其他地方。

语义可以解决。 其他语言有它。

C#的devise者决定简单地禁止静态的“goto-like”控制转移,从而简化一些东西。

但是,即使你这样做了,也不能解决如果从一个finally发起一个dynamic传输会发生什么情况的问题:如果finally块调用一个函数,那么这个函数会抛出什么呢? 原来的exception处理被“劫持”。

如果搞清楚这种第二种劫持forms的语义,那么没有理由放弃第一种forms。 他们真的是一样的东西:控制转移是控制转移,不pipe它是否是相同的词法范围。