在 C# 中传播在 finally 块中抛出的异常而不丢失来自 catch 块的异常的最佳实践是什么?

Ser*_*kov 6 c# exception-handling try-catch-finally

当一个异常可能在 finally 块中抛出时如何传播这两个异常 - 从 catch 和 from finally?

作为一种可能的解决方案 - 使用 AggregateException:

internal class MyClass
{
    public void Do()
    {
        Exception exception = null;
        try
        {
            //example of an error occured in main logic
            throw new InvalidOperationException();
        }
        catch (Exception e)
        {
            exception = e;
            throw;
        }
        finally
        {
            try
            {
                //example of an error occured in finally
                throw new AccessViolationException();
            }
            catch (Exception e)
            {
                if (exception != null)
                    throw new AggregateException(exception, e);
                throw;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这些异常可以像下面的代码片段那样处理:

private static void Main(string[] args)
{
    try
    {
        new MyClass().Do();
    }
    catch (AggregateException e)
    {
        foreach (var innerException in e.InnerExceptions)
            Console.Out.WriteLine("---- Error: {0}", innerException);
    }
    catch (Exception e)
    {
        Console.Out.WriteLine("---- Error: {0}", e);
    }

    Console.ReadKey();
}
Run Code Online (Sandbox Code Playgroud)

Dwe*_*rly 2

正如评论所暗示的那样,这可能表明“不幸的是”结构化代码。例如,如果您发现自己经常处于这种情况,则可能表明您试图在自己的方法中做太多事情。如果您无能为力,您只想抛出异常(您的代码“陷入”了无法通过编程解决的问题。您只想在合理期望您可以做一些有用的事情时捕获异常. 框架中有一个 OutOfMemoryException,但你很少会看到人们试图捕获它,因为在大多数情况下,这意味着你已经筋疲力尽了:-)

如果finally块中的异常是try块中异常的直接结果,则返回该异常只会使真正的问题变得复杂或模糊,从而使其更难以解决。在极少数情况下,存在返回的验证原因(例如异常),那么使用 AggregateException 将是执行此操作的方法。但在采用这种方法之前,请先问问自己是否可以将异常分离到单独的方法中,在这些方法中可以(单独)返回和处理单个异常。

  • 我强烈不同意这表明代码结构不良。一个简单的例子是,当您需要在finally块中处置某些内容时,并且您希望确保处置本身也顺利进行,无论该块之前是否有任何异常。无论您如何构建代码,“无论发生什么情况都做某事并处置关键资源”的要求很难分成不同的范围,而不必最终处理 try/catch/finally 。那么,既然我们无论如何都要面对问题,为什么还要逃避呢? (2认同)