在 C# 中,如果关联的 catch 块引发异常,是否可以强制控制通过 finally 块

本文关键字:控制 finally 是否 如果 关联 catch 异常 | 更新日期: 2023-09-27 18:34:23

我知道在 Java 中,如果异常被 catch 子句捕获并且其 catch 块抛出异常,则控制将在线程终止之前传递抛出关联的最终块(如果有(。但是,在 C# 中似乎并非如此。

在 C# 中,通过将

try-finally 语句与引发异常的 catch 块放在 try-catch 语句的 try 块中,几乎可以镜像这种行为,但如果例如,finally 块应该包含释放应该记录异常的流编写器的代码,这将是一个问题。

有没有一种干净的方法可以在 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 块内的某行,您将看到它不会被命中。(我使用的是VS2005(。

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

如果未捕获异常并允许终止应用程序(如我提供的示例代码所示(,则控件不会通过 finally 块。

在爪哇会这样。在 C# 中,至少根据我所看到的,它不会。

在 C# 中,如果关联的 catch 块引发异常,是否可以强制控制通过 finally 块

不,这是不正确的。C# 将始终执行finally块,即使在从catch块引发/重新引发异常后也是如此。请参阅如果从 catch 块抛出异常,何时最终运行?。

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

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

try
{
  ...
}
catch
{
  throw;
}

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

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

这是你要找的吗?

如前所述,一旦执行了 catch 块,finally 块将始终运行。

编辑:我刚刚在控制台应用程序中尝试了OP提供的示例代码,瞧,它没有命中最终块,并且出现"控制台应用程序1中发生了类型为'System.Exception'的未处理异常.exe"的错误。这真的很令人费解(除了在无限循环中重新抛出相同的异常的部分(,所以我做了一些调查,这就是我发现的:

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

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

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