如果从catch块抛出异常,何时最终运行?


134
try {
   // Do stuff
}
catch (Exception e) {
   throw;
}
finally {
   // Clean up
}

在上面的块中,何时调用finally块?在抛出e之前还是最终被调用然后捕获?


14
ps你不应该“抛出e”。因为那样会弄乱原始异常的堆栈跟踪。您应该只是“抛出;”。或创建一个新的异常,然后在将其抛出之前将InnerException设置为“ e”。
Erv Walter

24
如果最后没有运行,最后将是一个非常差的关键字选择,不是吗?
Eric Lippert

@ErvWalter还是这样吗?我正在VS2017中对这两种方式进行测试,而且看起来完全一样。您能否提供更多信息或参考?谢谢
Jeff Puckett

只是命名建议使用Exception ex-为事件/代理人保留e
R先生

Answers:


137

在e被重新抛出后(即执行catch块之后)将被调用

在这7年后进行编辑-一个重要的注意事项是,如果e没有被调用堆栈上方的try / catch块捕获或未被全局异常处理程序处理,则该finally可能根本不会执行。


18
而且永远不会调用Envrionment.FailFast()
约翰内斯·鲁道夫

16
在布兰登的答案试图代码后,我看到,finally如果在前面抛出的异常不执行catch永远不会陷入在外try- catch块!
安德鲁

3
感谢您编辑(接受的)答案以包括新信息。
Gordon Bean

3
他们(Microsoft)在新的文档站点上讨论了此问题: docs.microsoft.com/en-us/dotnet/csharp/language-reference/…:“在已处理的异常中,可以保证关联的finally块可以运行。但是,如果未处理异常,则finally块的执行取决于异常展开操作的触发方式,而这又取决于计算机的设置方式。”
DotNetSparky

1
请注意,“在调用堆栈后面被try / catch块捕获”将包括框架处理程序,例如ASP.NET或测试运行器中的那些。放置它的更好方法可能是“如果您的程序在catch块之后继续运行,则将执行finally块”。
ArrowCase

90

为什么不尝试一下:

outer try
inner try
inner catch
inner finally
outer catch
outer finally

带代码(格式化为垂直空间):

static void Main() {
    try {
        Console.WriteLine("outer try");
        DoIt();
    } catch {
        Console.WriteLine("outer catch");
        // swallow
    } finally {
        Console.WriteLine("outer finally");
    }
}
static void DoIt() {
    try {
        Console.WriteLine("inner try");
        int i = 0;
        Console.WriteLine(12 / i); // oops
    } catch (Exception e) {
        Console.WriteLine("inner catch");
        throw e; // or "throw", or "throw anything"
    } finally {
        Console.WriteLine("inner finally");
    }
}

5
+1,对于这么简单的事情,您实际上应该像Marc一样尝试一下。GJ用嵌套的try / catch / finally来说明:)
艾伦·赖斯

1
@AllenRice,如果Marc已经拥有,为什么还要尝试,而我可以在Google上找到Marc的答案?也许更好,请尝试一下,然后创建一个SO问题并自己回答,以造福他人。
joshden

8
请注意,如果您没有在外部捕获中捕获异​​常,则内部永远不会执行!在这种情况下,输出为outer try inner try inner catch Unhandled Exception: System.DivideByZeroException...
Andrew

1
@安德鲁,你是对的。您可以在这里找到说明:MSDN杂志2008年9月:CLR中的未处理异常处理(要打开chm,需要解锁:文件属性->常规->解锁)。如果将外部catch块替换为“ catch(ArgumentException)”,则不会再执行一个finally块,因为CLR无法找到任何“同意处理该异常的异常处理程序” DivideByZeroException。
vladimir

35

看完所有答案后,看来最终答案取决于

  • 如果您在catch块中重新抛出异常,并且该异常被捕获在另一个catch块中,则所有操作均根据文档执行。

  • 但是,如果未处理重引发的异常,则将永远不会执行。

我在带有C#4.0的VS2010中测试了此代码示例

static void Main()
    {
        Console.WriteLine("Example 1: re-throw inside of another try block:");

        try
        {
            Console.WriteLine("--outer try");
            try
            {
                Console.WriteLine("----inner try");
                throw new Exception();
            }
            catch
            {
                Console.WriteLine("----inner catch");
                throw;
            }
            finally
            {
                Console.WriteLine("----inner finally");
            }
        }
        catch
        {
            Console.WriteLine("--outer catch");
            // swallow
        }
        finally
        {
            Console.WriteLine("--outer finally");
        }
        Console.WriteLine("Huzzah!");

        Console.WriteLine();
        Console.WriteLine("Example 2: re-throw outside of another try block:");
        try
        {
            Console.WriteLine("--try");
            throw new Exception();
        }
        catch
        {
            Console.WriteLine("--catch");
            throw;
        }
        finally
        {
            Console.WriteLine("--finally");
        }

        Console.ReadLine();
    }

这是输出:

示例1:在另一个try块中重新抛出:--
outer try
---- inner try
---- inner catch
---- inner finally
--outer catch
--outer finally
Huzzah!

示例2:重新抛出另一个try块之外:--
try
--catch

未处理的异常:System.Exception:引发了类型为“ System.Exception”的异常。
在ConsoleApplication1.Program.Main()中的C:\ local source \ ConsoleApplication1 \ Program.cs:line 53中


3
很好,我没有意识到!
安德鲁

1
请注意,最后一个最终结果可能会运行,具体取决于您选择的内容:stackoverflow.com/a/46267841/480982
Thomas Weller

1
有趣的...在.NET Core 2.0上,最后一部分在未处理的异常之后运行。
Mahdi Ghiasi '18

有趣的是,我刚刚在.NET Core 2.0和.NET Framework 4.6.1中都进行了测试,并且它们都在未处理的异常之后运行finally。这种行为改变了吗?
卡梅隆·贝尔斯坦

24

您的示例的行为与以下代码相同:

try {
    try {
        // Do stuff
    } catch(Exception e) {
        throw e;
    }
} finally {
    // Clean up
}

作为一个侧面说明,如果你真正的意思throw e;(即扔你刚好赶上了同样的异常),它是好,只是这样做throw;,因为这将保留原始的堆栈跟踪,而不是创建一个新的。


我不认为这是正确的。最后应该在外部尝试块内而不是外部
Matthew Pigram

@MatthewPigram:你是什么意思?finally实际上,该块将在该块之后运行catch(即使catch块抛出异常),这就是我的代码段试图说明的内容。
Daniel Pryden 2014年

从我如何解释他的例子来看,他正试图在另一个try块中最终进行try catch。最终不是一个尝试之内的尝试
Matthew Pigram 2014年

1
@MatthewPigram:我的回答根本没有任何“ try-catch-finally”结构。它有一个“ try-finally”,在try我的回答里面有一个“ try-catch”。我试图通过使用两个2部分结构来解释3部分结构的行为。我try在原始问题中没有看到第二个障碍的迹象,所以我不明白您从哪里得到的。
Daniel Pryden 2014年

12

如果在catch处理程序块中存在未处理的异常,则finally块将被精确地调用0

  static void Main(string[] args)
  {
     try
     {
        Console.WriteLine("in the try");
        int d = 0;
        int k = 0 / d;
     }
     catch (Exception e)
     {
        Console.WriteLine("in the catch");
        throw;
     }
     finally
     {
        Console.WriteLine("In the finally");
     }
  }

输出:

C:\ users \ administrator \ documents \ TestExceptionNesting \ bin \ Release> TestExceptionNesting.exe

在尝试

抓住

未处理的异常:System.DivideByZeroException:试图除以零。在C:\ users \ administrator \ documents \ TestExceptionNesting \ TestExceptionNesting.cs:line 22中的TestExceptionNesting.Program.Main(String [] args)

C:\ users \ administrator \ documents \ TestExceptionNesting \ bin \ release>

我今天在一次面试中被问到这个问题,面试官一直回头说:“你确定最后没有被打来吗?” 我不确定这是否是一个棘手的问题,或者面试官是否还有其他想法,并为我编写了错误的代码来进行调试,所以我回到家尝试了一下(构建和运行,没有调试器交互),只是想着想休息。


除非抛出的异常在堆栈中更高的位置被另一个捕获捕获,否则在这种情况下,如果该抛出的异常得到处理,它可能会执行...否则我可能是错的...
tomosius 2015年

@tomosius,是的,这就是布兰登的答案所解释的。:)
安德鲁

这就是为什么我从指定“如果存在未处理的异常”开始。如果抛出的异常在某处被捕获,那么根据定义,我们正在谈论另一种情况。
Eusebio Rufian-Zilbermann

这不是真的。至少不是针对NET Core 3.1。一个带有此代码的简单新控制台项目在异常之后显示“ in the finally”。
emzero

有趣的是,行为发生了变化,当我发布时,.NET核心甚至都不存在;)
Eusebio Rufian-Zilbermann

2

一种简单的告诉方法也就是调试代码,并注意何时调用finally。


1

使用C#控制台应用程序进行测试,在引发异常之后,将执行finally代码:存在“应用程序错误对话框”,并且在选择“关闭程序”选项之后,在该控制台窗口中执行了finally块。但是将断点设置在finally代码块中,我永远无法实现。调试器不断在throw语句处停止。这是我的测试代码:

    class Program
    {
       static void Main(string[] args)
       {
          string msg;
          Console.WriteLine(string.Format("GetRandomNuber returned: {0}{1}", GetRandomNumber(out msg), msg) == "" ? "" : "An error has occurred: " + msg);
       }

       static int GetRandomNumber(out string errorMessage)
       {
         int result = 0;
         try
         {
            errorMessage = "";
            int test = 0;
            result = 3/test;
            return result;
         }
         catch (Exception ex)
         {
            errorMessage = ex.Message;
            throw ex;

         }
         finally
         {
            Console.WriteLine("finally block!");
         }

       }
    }

VS2010中的调试-.NET Framework 4.0

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.