我正在编写一些大量使用异常来处理某些错误情况的代码(当然这不是最好的设计,但我正在使用它)。
当代码中发生异常时,我需要一种优雅的方式来清理所有打开的或临时的资源。
这可以像这样执行:
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()
首先,因为 foo
资源已经被清理,它本身会抛出异常。
这意味着 cleanupResourceBar()
不会被调用,我们将以资源泄漏告终。
当然我们可以像这样重写内部的 try/catch
block :
try
{
cleanupResourceFoo();
}
catch
{
}
try
{
cleanupResourceBar();
}
catch
{
}
但现在我们变得很丑陋。
我有 C++ 背景,这是我通常会使用 RAII 的那种东西。关于在 C# 中处理此问题的优雅方式的任何建议?
最佳答案
清理资源应该几乎总是通过 using
语句和 IDisposable
来处理 - 所以你只需:
using (FirstResource r1 = ...)
{
using (SecondResource r2 = ...)
{
...
}
}
如果您只想在出现异常时清理资源,这种情况比较少见——而且我不希望 RAII 在 C++ 中特别帮助您。您可以使用委托(delegate)来简化此操作:
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
并继续:捕获特定您在特定情况下期望的异常。显然,您的情况可能非常特殊,但这是在大多数情况下都适用的一般性建议。如果确实需要,您可以重构上面的辅助方法以捕获异常。
关于c# - 可能抛出异常时清理多个资源的优雅方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7412153/