【问题标题】:When is finally run if you throw an exception from the catch block?如果从 catch 块中抛出异常,最终何时运行?
【发布时间】:2010-12-06 01:10:00
【问题描述】:
try {
   // Do stuff
}
catch (Exception e) {
   throw;
}
finally {
   // Clean up
}

在上面的块中,finally块是什么时候调用的?在抛出 e 或最后被调用然后 catch 之前?

【问题讨论】:

  • 附注你不应该“扔 e;”因为这会弄乱原始异常的堆栈跟踪。你应该只是“扔;”。或者创建一个新异常并将 InnerException 设置为“e”,然后再抛出它。
  • finally 如果它没有运行 last 将是一个非常糟糕的关键字选择,你不是说吗?
  • @ErvWalter 这仍然是真的吗?我在VS2017中以两种方式测试它,它似乎完全一样。您能否提供更多信息或参考?谢谢
  • 只是命名建议使用 Exception ex - 为事件/代表保留 e

标签: c#


【解决方案1】:

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

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

【讨论】:

  • 如果你调用 Envrionment.FailFast() 则永远不会
  • 在尝试了 Brandon 的回答中的代码后,我发现如果前面的 catch 中抛出的异常从未在外部 @ 中捕获,则不会执行 finally 987654326@-catch块!
  • 感谢您编辑(已接受)答案以包含新信息。
  • 他们(微软)在新的文档站点上讨论了它:docs.microsoft.com/en-us/dotnet/csharp/language-reference/…:“在处理的异常中,保证运行相关的 finally 块。但是,如果异常未处理,则执行finally 块的大小取决于异常展开操作的触发方式。而这又取决于您的计算机的设置方式。”
  • 请注意,“被调用堆栈中的 try/catch 块捕获”将包括框架处理程序,例如 ASP.NET 或测试运行程序中的处理程序。一个更好的说法可能是“如果你的程序在 catch 块之后继续运行,那么 finally 块将执行。”
【解决方案2】:

为什么不试试呢:

这是执行以下代码的输出:

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

【讨论】:

  • +1,对于这么简单的事情,你真的应该像 Marc 一样尝试一下。 GJ 用嵌套的 try/catch/finally 来说明它 :)
  • @AllenRice 如果 Marc 已经有了,为什么要尝试它,而我可以谷歌找到 Marc 的答案?或者也许更好,尝试自己,然后创建一个 SO 问题并自己回答,以帮助他人。
  • 请注意,如果你没有在外层catch中捕获异常,内层finally永远不会被执行!!在这种情况下,输出为outer tryinner tryinner catchUnhandled Exception: System.DivideByZeroException...
  • @Andrew 你是对的。你可以在这里找到解释MSDN Magazine 2008 September: Unhandled Exception Processing in the CLR(打开chm需要解锁:文件属性->常规->解锁)。如果将外部 catch 块替换为“catch (ArgumentException)”,则不会再执行任何 finally 块,因为 CLR 找不到任何“同意处理异常的异常处理程序” DivideByZeroException。
【解决方案3】:

在这里阅读所有答案后,看起来最终答案是取决于

  • 如果您在 catch 块中重新引发异常,并且该异常在另一个 catch 块中被捕获,则一切都将根据文档执行。

  • 但是,如果重新计算异常未处理,则 finally 永远不会执行。

我在 VS2010 w/C# 4.0 中测试了这个代码示例

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 最后
嘘!

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

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

【讨论】:

  • 很好,我没有意识到这一点!
  • 请注意,最后一个 finally 可能会运行,具体取决于您选择的内容:stackoverflow.com/a/46267841/480982
  • 有趣...在 .NET Core 2.0 上,finally 部分在未处理的异常之后运行。
  • 有趣的是,我刚刚在 .NET Core 2.0 和 .NET Framework 4.6.1 中进行了测试,它们都在未处理的异常之后运行 finally。这种行为有改变吗?
  • 我刚刚在 .Net 5.0 中运行了您的测试,结果如下: 示例 2:在另一个 try 块之外重新抛出:--try --catch 未处理的异常。 System.Exception:引发了“System.Exception”类型的异常。在 \Program.cs:line 36 中的 finallyTest.Program.Main() --finally 在第二个测试中运行 finally 块
【解决方案4】:

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

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

附带说明,如果您真的是指 throw e;(即抛出您刚刚捕获的相同异常), 最好只使用 throw;,因为这样可以保留原始堆栈跟踪,而不是创建一个新的。

【讨论】:

  • 我不认为这是正确的。 finally 应该在外部 try 块内而不是在它之外
  • @MatthewPigram:你什么意思? finally 块实际上将在 catch 块之后运行(即使 catch 块重新抛出异常),这就是我的 sn-p 试图说明的内容。
  • 从我如何解释他的例子来看,他最终试图在另一个 try 块中进行 try catch。不是 try catch finally 中的 try catch
  • @MatthewPigram:我的答案根本没有任何“try-catch-finally”结构。它有一个“try-finally”,在我的答案的try 块内有一个“try-catch”。我试图通过使用两个 2 部分构造来解释 3 部分构造的行为。我在原始问题中没有看到任何第二个 try 块的迹象,所以我不明白你从哪里得到的。
【解决方案5】:

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

  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:试图除法 由零。在 TestExceptionNesting.Program.Main(String[] args) 中 C:\users\administrator\documents\TestExceptionNesting\TestExceptionNesting.cs:22 行

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

我今天在面试中被问到这个问题,面试官一直在问“你确定 finally 没有被调用吗?”我不确定这是否意味着一个技巧问题,或者面试官有其他想法并为我编写了错误的代码来调试,所以我回家尝试了它(构建并运行,没有调试器交互),只是为了让我的想法休息。

【讨论】:

  • 除非抛出的异常在堆栈更高的某个地方被另一个catch捕获,在这种情况下,如果抛出的异常得到处理,它可能会执行......或者我可能错了......
  • @tomosius,是的,这就是布兰登的回答所解释的。 :)
  • @tomosius 这就是为什么我开始指定“如果有未处理的异常”。如果抛出的异常在某处被捕获,那么根据定义,我们谈论的是另一种情况。
  • 这不是真的。至少对于 NET Core 3.1 来说不是。带有此代码的简单新控制台项目在异常后显示“in the finally”。
  • 有趣的是,行为发生了变化,当我发布时.NET core 甚至都不存在;)
【解决方案6】:

一个简单的判断方法是调试您的代码并注意何时调用 finally。

【讨论】:

    【解决方案7】:

    使用 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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-11-22
      • 1970-01-01
      • 2020-12-16
      • 1970-01-01
      • 2012-08-22
      • 2012-11-14
      • 2011-03-18
      相关资源
      最近更新 更多