我没有问题,只是好奇。想象以下情况:
foreach (var foo in list)
{
try
{
//Some code
}
catch (Exception)
{
//Some more code
}
finally
{
continue;
}
}
由于出现编译错误 CS0157,因此此代码无法编译:
控件不能离开finally子句的主体
为什么?
我没有问题,只是好奇。想象以下情况:
foreach (var foo in list)
{
try
{
//Some code
}
catch (Exception)
{
//Some more code
}
finally
{
continue;
}
}
由于出现编译错误 CS0157,因此此代码无法编译:
控件不能离开finally子句的主体
为什么?
finally
块无论是否抛出异常,都会执行。如果抛出异常,那么 continue
会发生什么?你不能继续执行循环,因为未捕获的异常将转移控制权到另一个函数。finally
在 try/catch 块内运行其他控制传输语句时也会运行,例如 return
,这带来了相同的问题。finally
的语义,不允许从 finally
块内部转移控制到其外部是没有意义的。foreach ( var in list )
{
try{
//some code
}catch{
continue;
}
}
如果你想在没有未捕获异常时继续进行,只需将continue
放在try块之外。
yield
和await
与try
块之间也存在类似的限制。 - Eric Lippert这里有一个可靠的来源:
continue语句不能退出finally块(第8.10节)。当在finally块中出现continue语句时,continue语句的目标必须在同一个finally块内;否则,将出现编译时错误。
这段内容摘自MSDN 的 8.9.2 The continue statement。
文档说:
当控制权离开try语句时,finally块的语句总是会执行。无论控制转移是否因正常执行、由于执行break、continue、goto或return语句或由于将异常传播到try语句之外而发生。如果在执行finally块期间抛出异常,则将该异常传播到下一个封闭的try语句。如果另一个异常正在传播过程中,则该异常将丢失。异常传播过程在throw语句(第8.9.5节)的描述中进一步讨论。
内容来自 8.10 The try statement。
你可能认为它有道理,但实际上没有道理。
foreach (var v in List)
{
try
{
//Some code
}
catch (Exception)
{
//Some more code
break; or return;
}
finally
{
continue;
}
}
当抛出异常时,你打算使用 break 还是 continue?C# 编译器小组不希望自行决定使用 break
还是 continue
。相反,他们决定抱怨开发人员的情况将会不明确,因为从 finally块
转移控制。
因此,开发人员的工作是清楚地说明他想要做什么,而不是编译器假设其他内容。
我希望你能理解为什么这不能编译!
finally
语句后面执行的路径也会受到catch
是否通过异常退出的影响,并且没有机制可以说明它应该如何与continue
交互。我喜欢你的例子,因为它展示了一个更大的问题。 - supercat正如其他人所说,但是重点放在异常处理上,这实际上是关于传递控制的模糊处理。
在您心中,您可能会想到以下场景:
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
块。语言设计者认为这会过于模糊不清,并且(很可能)无法在编译时确保你的continue
仅在控制流未被转移的情况下使用。finally
块中使用 continue
是没有意义的。看一下这个例子:foreach (var item in list)
{
try
{
throw new Exception();
}
finally{
//doesn't make sense as we are after exception
continue;
}
}
continue
语句在循环语句之外没有意义,但这并不意味着它不被支持。 - zerkmsitem
可以是文件,读取失败-> finally
关闭文件。continue
阻止其余的处理。 - jnovacho"这个代码无法编译,但我认为它是完全有道理的"
我觉得并不是这样。
当你使用 catch(Exception)
的时候,你就不需要 finally (甚至可能不需要 continue
)。
当你使用更现实的 catch(SomeException)
时,如果一个异常没有被捕获,那么应该发生什么?你的continue
想要走一条路,而异常处理则另外一条路。
catch
时,你可能需要使用finally
。它通常用于可靠地关闭资源。 - jnovachotry
或catch
块中轻松地使用return
语句。现在我们该怎么办?返回还是继续循环?(如果我们继续,如果它是最后一个要迭代的项目呢?那么我们就会在foreach
之外继续,而返回永远不会发生?)编辑:甚至可以使用goto
到循环外的标签,几乎任何将控制转移出foreach
循环的操作。 - Chris Sinclairreturn
。但通常在 catch-all 之后继续执行。 - H Hfinally块中不能离开其所属的函数体,包括break、return和在您的情况下continue关键字。
finally
语句块可能会在等待重新抛出的异常情况下执行。如果没有重新抛出异常,那么退出该语句块(通过continue
或其他方式)就没有意义。finally
语句:只需捕获异常并不重新抛出即可。finally
无论是否抛出未捕获的异常都会执行。其他人已经解释了为什么这使得continue
不合逻辑,但是这里有一个替代方案,它遵循了这段代码所要求的精神。基本上,finally { continue; }
的意思是:
catch
的末尾放置continue
来满足,而(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
中放置。
finally
块中需要使用continue;
?这不与try-catch
块后面的continue;
相同吗? - bansifinally/continue
是 C# 编译器的限制 :-) 我对这种限制的原因也感到好奇。 - xanatosbr
分支指令系列无法完成此操作。 - Unsigned