0

我知道在Java中,如果一个异常被一个catch子句捕获并且它的catch块抛出一个异常,控制将在线程终止之前传递相关的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 块中另一个 try-catch 语句)。

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

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

4

4 回答 4

2

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

于 2013-10-08T00:04:28.287 回答
1

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

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

try
{
  ...
}
catch
{
  throw;
}

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

于 2013-12-13T18:27:31.817 回答
0

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

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

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

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

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

于 2013-10-08T02:42:20.473 回答
0

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

这是要找的。

于 2013-10-08T00:03:44.093 回答