关于c#:如果从catch块中抛出异常,最后会运行什么?


When is finally run if you throw an exception from the catch block?

1
2
3
4
5
6
7
8
9
try {
   // Do stuff
}
catch (Exception e) {
   throw;
}
finally {
   // Clean up
}

在上面的块中,何时调用finally块?在投掷之前还是最后被召唤然后被抓住?


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

在7年后编辑这篇文章-一个重要的注意事项是,如果在调用堆栈上更远处的try/catch块没有捕获到e,或者没有由全局异常处理程序处理,那么finally块可能根本就不会执行。


为什么不试试呢?

1
2
3
4
5
6
outer try
inner try
inner catch
inner finally
outer catch
outer finally

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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");
    }
}


在阅读了所有的答案之后,最终的答案似乎取决于:

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

  • 但是,如果重新生成的异常未经处理,则最终不会执行。

我在VS2010 w/c_4.0中测试了这个代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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();
    }

以下是输出:

Example 1: re-throw inside of another try block:
--outer try
----inner try
----inner catch
----inner finally
--outer catch
--outer finally
Huzzah!

Example 2: re-throw outside of another try block:
--try
--catch

Unhandled Exception: System.Exception: Exception of type 'System.Exception' was thrown.
at ConsoleApplication1.Program.Main() in C:\local source\ConsoleApplication1\Program.cs:line 53


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

1
2
3
4
5
6
7
8
9
try {
    try {
        // Do stuff
    } catch(Exception e) {
        throw e;
    }
} finally {
    // Clean up
}

作为旁注,如果您真正指的是throw e;(即抛出刚才捕获的异常),那么最好只执行throw;,因为这样可以保留原始堆栈跟踪而不是创建新的堆栈跟踪。


如果在catch处理程序块内有未处理的异常,那么finally块将被精确调用零次。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  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
elease>TestExceptionNesting.exe

in the try

in the catch

Unhandled Exception: System.DivideByZeroException: Attempted to divide
by zero. at TestExceptionNesting.Program.Main(String[] args) in
C:\users\administrator\documents\TestExceptionNesting\TestExceptionNesting.cs:line 22

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

我今天在一次面试中被问到这个问题,面试官不停地回问:"你确定最后一个不会被打电话吗?"我不确定这是否意味着一个技巧问题,或者面试官有其他想法,并为我编写了错误的代码进行调试,所以我回到家并尝试了它(构建和运行,没有调试程序交互),只是为了让我的头脑休息一下。


一个简单的方法是调试代码,并注意最后一次调用的时间。


使用C控制台应用程序进行测试时,最终代码在引发异常后执行:存在"应用程序错误对话框",选择"关闭程序"选项后,最终块在该控制台窗口中执行。但是在finally代码块中设置断点,我永远也打不到它。调试器一直在throw语句处停止。这是我的测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    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中调试