【问题标题】:in C# is it possible to force control to pass through a finally block if an exception is thrown by an associated catch block?在 C# 中,如果关联的 catch 块引发异常,是否可以强制控制通过 finally 块?
【发布时间】:2013-10-08 00:01:30
【问题描述】:

我知道在 Java 中,如果一个异常被 catch 子句捕获,并且它的 catch 块抛出一个异常,控制将在线程终止之前通过 throw 关联的 finally 块(如果有的话)。然而,这在 C# 中似乎并非如此。

几乎可以在 C# 中反映这种行为,方法是在 try-catch 语句的 try 块内放置一个 try-finally 语句,并带有引发异常的 catch 块,但如果出现以下情况,这将是一个问题,例如, finally 块应该包含处理应该记录异常的 Stream Writer 的代码。

有没有一种简洁的方法可以在 C# 中实现类似 java 的 try-catch-finally 异常处理行为?

以下是请求的示例代码的更新:

StreamWriter writer = new StreamWriter("C:\\log.txt");
try
{
    throw new Exception();
}
catch (Exception e)
{
    writer.WriteLine(e.Message);
    throw e;
}
finally
{
    if (writer != null)
    {
       writer.Dispose();
    }
}

将该代码添加到控制台应用程序,运行它,不处理重新抛出的异常,尝试删除 C:\log.txt。您将无法做到,因为控制从未通过 finally 块。此外,如果您在 finally 块内的某行添加断点,您会看到它没有被命中。 (我用的是VS2005)。

据我所知,强制控制通过 finally 块的唯一方法是,如果重新抛出的异常由封闭 try 块的 catch 块处理(如果您将上面的代码放在里面另一个 try-catch 语句的 try 块)。

如果异常没有被捕获并被允许终止应用程序,就像我提供的示例代码一样,控制将不会通过 finally 块。

在 Java 中它会。在 C# 中,至少根据我所见,它不会。

【问题讨论】:

  • 它们的行为相同(至少如果我正确理解您的情况)。即使 catch 块抛出,关联的 finally 块也会被执行。如果您提供示例代码,您的问题会更清楚。

标签: c# java exception try-catch-finally


【解决方案1】:

不,这是不正确的。 C# 将始终执行 finally 块,即使在从 catch 块抛出/重新抛出异常之后也是如此。见When is finally run if you throw an exception from the catch block?

【讨论】:

    【解决方案2】:

    在 .NET Framework 中,当异常发生时,系统将在任何 finally 块执行之前确定是否有任何东西将捕获该异常。根据不同的应用程序设置,尝试抛出不会被捕获的异常可能会立即终止应用程序,而不会给任何finally 块(或其他任何东西)运行机会。

    如果一个包装Main方法,以及每个线程,在

    try
    {
      ...
    }
    catch
    {
      throw;
    }
    

    那么在try 块中抛出的任何异常都会被捕获。即使它会立即被重新抛出,任何嵌套的finally 块都将在catch 之前执行。在某些情况下,这是理想的行为;在其他情况下,人们可能希望这样做,例如如果不会捕获异常,则执行一些特殊的日志记录(在某些情况下,如果finally 块有机会首先运行,则希望记录的信息可能会被破坏)。在 C# 中,没有任何方法可以根据是否捕获异常来改变操作,但在 VB.NET 中有一些方法可以做到这一点;调用 C# 代码的 VB.NET 程序集可以让该代码知道内部方法抛出的任何异常是否会传播到 vb.net 包装器而不会被捕获。

    【讨论】:

    • 您的解释与我观察到的行为一致。这在 .NET Framework 的规范中吗?另外,您提到的应用程序设置是什么?如何配置它们?
    • @JSmith:我读过一些设置,但不记得细节了,很遗憾。 ECMA 规范明确定义了在发现异常未处理之前发生的情况;在那之后,一个实现可以(就规范而言)做它喜欢的事情,IIRC。
    【解决方案3】:

    但在 C# 中似乎并非如此。

    this 你在找什么。

    【讨论】:

      【解决方案4】:

      如前所述,finally 块将始终在 catch 块执行后运行。

      编辑:我刚刚在控制台应用程序中尝试了 OP 提供的示例代码,你瞧,它没有命中 finally 块,并且出现错误“在 ConsoleApplication1 中发生‘System.Exception’类型的未处理异常。可执行程序”。这确实令人费解(除了在无限循环中重新抛出相同异常的部分)所以我做了一些调查,这就是我发现的:

      如果发生异常,CLR 会向上遍历调用堆栈以查找 匹配的 catch 表达式。如果 CLR 没有找到匹配的, 或者每次都重新抛出异常,异常冒泡 Main() 方法。在这种情况下,Windows 会处理异常。

      控制台应用程序的事件处理是最容易理解的, 因为CLR没有特殊处理。例外是 如果没有被抓住,离开应用程序线程。 CLR 打开一个窗口 要求调试或退出应用程序。如果用户选择 debug,调试器启动。如果用户选择关闭, 应用程序退出,异常被序列化并写入 控制台。

      故事的寓意,不要从控制台应用程序的 catch 块中重新抛出相同的异常!。

      【讨论】:

      • “如前所述,finally 块将始终在 catch 块执行后运行。”根据规范,这可能是正确的,但在实践中似乎并不总是正确的。
      • @J Smith 更新了答案。
      猜你喜欢
      • 1970-01-01
      • 2010-10-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-31
      • 2011-02-01
      • 2012-05-02
      • 2017-09-04
      相关资源
      最近更新 更多