【问题标题】:Elegant way of cleaning up multiple resources when exceptions may be thrown当可能抛出异常时清理多个资源的优雅方法
【发布时间】:2011-11-16 17:59:48
【问题描述】:

我正在编写一些代码,这些代码大量使用异常来处理某些错误条件(无论如何肯定不是最好的设计,但我正在使用它)。

当代码中发生异常时,我需要一种优雅的方式来清理任何打开的或临时的资源。

这可以这样执行:

try
{
    foo();
    bar();
}
catch (Exception)
{
    // Oops, an error occurred - let's clean up resources
    // Any attempt to cleanup non-existent resources will throw
    // an exception, so let's wrap this in another try block
    try
    {
        cleanupResourceFoo();
        cleanupResourceBar();
    }
    catch
    {
        // A resource didn't exist - this is non-fatal so let's drop
        // this exception
    }
}

假设foo() 方法正确清理了自身,但bar() 方法抛出了异常。在清理代码中,我们将调用cleanupResourceFoo() first,它本身会抛出异常,因为foo 资源已经被清理了。

这意味着cleanupResourceBar() 最终不会被调用,我们最终会导致资源泄漏。

当然我们可以像这样重写内部的try/catch 块:

try
{
    cleanupResourceFoo();
}
catch
{
}
try
{
    cleanupResourceBar();
}
catch
{
}

但现在我们变得很丑。

我来自 C++ 背景,这是我通常会使用 RAII 来做的事情。关于在 C# 中处理此问题的优雅方式有什么建议吗?

【问题讨论】:

    标签: c# exception resources


    【解决方案1】:

    清理资源几乎总是应该通过using 语句和IDisposable 来处理 - 所以你只需:

    using (FirstResource r1 = ...)
    {
        using (SecondResource r2 = ...)
        {
            ...
        }
    }
    

    如果您想在异常情况下清理资源,这有点少见——我不希望 RAII 在 C++ 中特别帮助您。您可能会使用委托来简化此操作:

    TryWithCleanUpOnException(foo, cleanUpResourceFoo);
    TryWithCleanUpOnException(bar, cleanUpResourceBar);
    
    ...
    
    private static void TryWithCleanUpOnException(Action action,
                                                  Action cleanUp)
    {
        bool success = false;
        try
        {
            action();
            success = true;
        }
        finally
        {
            if (!success)
            {
                cleanup();
            }
        }
    }
    

    通过不捕获异常,这允许错误传播而不是被吞没。这通常是你想要的——如果不是你的情况,也许你可以更准确地解释你的情况。

    您已经说过您实际上想要忽略一些非致命异常 - 但您通常不应该只捕获 Exception 并继续:捕获您期望的特定异常特殊情况。显然,您可能有一个非常特殊的情况,但这是大多数时间都适用的一般建议。如果你真的想要,你可以重构上面的帮助方法来捕获异常。

    【讨论】:

    • 好的,所以我了解using 如何限制资源的范围,但不确定您所说的IDisposable 是什么意思。我对 C# 的(有限)理解是大多数(?)对象都继承自 IDisposable。那是对的吗?它对我的情况有何帮助?
    • 看看这里:codeproject.com/KB/cs/idisposable.aspx 一个详细的例子。
    • 当程序到达作用域的末尾时,运行时调用Dispose() 方法,该方法在IDisposable 中声明。如果你想在你的资源中使用using,你需要继承这个接口并实现一种清理资源的方法
    • @LeopardSkinPillBoxHat:只有需要处理的类型才实现 IDisposable - 它专门用于指示类型确实需要处理,using 语句直接支持它。跨度>
    • 如果相关资源是数据库条目会怎样?例如foo() 会在方法的开头添加一个表格行,如果到了末尾就删除它,但是如果没有到末尾,我们需要在异常处理程序中删除它吗?这是您的委托方法最适合的地方吗?
    猜你喜欢
    • 2014-07-20
    • 2013-07-02
    • 2017-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-23
    • 1970-01-01
    相关资源
    最近更新 更多