C# 为什么可以';t a';继续';语句必须位于';最后';块

C# 为什么可以';t a';继续';语句必须位于';最后';块,c#,.net,C#,.net,我没有问题;我只是好奇。想象一下以下场景: foreach (var foo in list) { try { //Some code } catch (Exception) { //Some more code } finally { continue; } } 这不会编译,因为它会引发: 控件不能离开finally子句的主体 为什么?这里有一个可靠的来源: contin

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

foreach (var foo in list)
{
    try
    {
         //Some code
    }
    catch (Exception)
    {
        //Some more code
    }
    finally
    {
        continue;
    }
}
这不会编译,因为它会引发:

控件不能离开finally子句的主体


为什么?

这里有一个可靠的来源:

continue语句不能退出finally块(第8.10节)。什么时候 continue语句出现在finally块中,finally块是 continue语句必须位于同一finally块中;否则,一个 发生编译时错误

它取自MSDN

文件说:

finally块的语句总是在控件离开时执行 试试这句话。无论控制权转移是否作为 正常执行的结果,作为执行中断的结果, continue、goto或return语句,或作为传播 try语句中的异常。如果在过程中引发异常 执行finally块时,异常会传播到下一个 包含try语句。如果另一个异常正在处理中 在传播时,该异常将丢失。传播过程 在throw语句的描述(第8.9.5节)中进一步讨论了一个异常


从这里开始。

通常
continue
finally
块中使用时没有意义。看看这个:

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

不能离开finally块的主体。这包括break、return和continue关键字

“这不会编译,我认为它完全有意义”

嗯,我想没有

如果您确实有
catch(Exception)
,那么您就不需要finally(甚至可能不需要
continue


当您有更现实的
捕获(SomeException)
时,如果未捕获异常,会发生什么?您的
continue
想要走一条路,异常处理另一条路

你可能认为它有意义,但实际上它没有意义

foreach (var v in List)
{
    try
    {
        //Some code
    }
    catch (Exception)
    {
        //Some more code
        break; or return;
    }
    finally
    {
        continue;
    }
}
当抛出异常时,您打算如何执行中断继续?C#编译器团队不希望通过假设
中断
继续
来自行做出决定。相反,他们决定抱怨开发人员将控制权从
最终块
转移的情况不明确

因此,开发人员的工作是清楚地说明他打算做什么,而不是假设其他事情


我希望您理解为什么这不能编译

最后
无论是否引发异常,块都会运行。如果抛出异常,
continue
会做什么?无法继续执行循环,因为未捕获的异常会将控制权转移到另一个函数

即使没有引发异常,
finally
也会在try/catch块内的其他控制传输语句运行时运行,例如
return
,这会带来同样的问题

简而言之,使用
finally
的语义,允许将控制从
finally
块内部转移到块外部是没有意义的

用一些替代的语义来支持这一点会更加混乱而不是有帮助,因为有一些简单的变通方法可以使预期的行为方式更加清晰。所以你会犯错误,被迫正确地思考你的问题。这是C#中普遍存在的“把你扔进成功的深渊”的想法

如果要忽略异常(通常是一个坏主意)并继续执行循环,请使用catch-all块:

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

如果只想在没有引发未捕获异常的情况下
继续
,只需将
继续
放在try块之外。

最后块可以在等待重试的异常情况下执行。如果不重新触发异常,就能够退出块(通过
continue
或其他方式)是没有意义的


如果不管发生什么情况,您都想继续循环,那么不需要finally语句:只捕获异常,不要重复。

正如其他人所说,但重点是异常,它实际上是关于传输控制的模糊处理

在您的脑海中,您可能会想到这样一个场景:

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;
}
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);
}
理论上,您可以跟踪控制流并说,是的,这是“ok”。不会引发异常,也不会传输任何控件。但C#语言设计师们还考虑了其他问题

抛出的异常 可怕的后藤 回归 分手
总之,是的,虽然在控制权未转移的情况下使用
continue
的可能性很小,但大多数情况下都涉及异常或
return
块。语言设计者认为这太模糊了,而且(可能)不可能在编译时确保您的
continue
仅在控制流未被传输的情况下使用。

最后,无论是否引发未捕获的异常,都会运行
。其他人已经解释了为什么这使得
继续
不合逻辑,但这里有一个替代方案,它遵循了这段代码所要求的精神。基本上,
最后{continue;}
是说:

  • 如果捕获到异常,请继续
  • 当存在未捕获的异常时,允许抛出它们,但仍继续
  • (1) 可以通过将
    continue
    放在每个
    catch
    的末尾来满足,并且(2)可以通过存储未捕获的异常来满足,这些异常将在以后抛出。你可以这样写:

    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;
    }
    
    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);
    }
    
    var异常=新列表();
    foreach(列表中的变量foo){
    试一试{
    //一些代码
    }捕获(无效操作异常ex){
    //特定于句柄
    
    public static void Break()
    {
        foreach(var item in list)
        {
            try
            {
                break;
            }
            finally
            {
                continue;
            }
        }
    }
    
    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);
    }