我想提供一个管理创建和删除临时目录的类。理想情况下,我希望它可在 using 块中使用,以确保无论我们如何离开该块,都会删除该目录:
static void DoSomethingThatNeedsATemporaryDirectory()
{
using (var tempDir = new TemporaryDirectory())
{
// Use the directory here...
File.WriteAllText(Path.Combine(tempDir.Path, "example.txt"), "foo\nbar\nbaz\n");
// ...
if (SomeCondition)
{
return;
}
if (SomethingIsWrong)
{
throw new Exception("This is an example of something going wrong.");
}
}
// Regardless of whether we leave the using block via the return,
// by throwing and exception or just normally dropping out the end,
// the directory gets deleted by TemporaryDirectory.Dispose.
}
创建目录没有问题。问题在于如何编写Dispose方法。当我们尝试删除目录时,可能会失败;例如,因为我们仍然有一个文件在其中打开。但是,如果我们允许异常传播,它可能掩盖了在using块内发生的异常。特别地,如果在using块内发生异常,它可能是导致我们无法删除目录的异常之一,但如果我们掩盖它,我们就失去了修复问题最有用的信息。
看起来我们有四个选择:
- 捕获并忽略任何试图删除目录时的异常。我们可能没有意识到我们未能清理临时目录。
- 以某种方式检测Dispose是否作为堆栈展开的一部分运行,此时抛出异常,并合并IOException和其他抛出的异常。可能不可行。(我提到这一点部分原因是因为Python的上下文管理器可以做到这一点,在许多方面类似于用C#的using语句使用的.NET中的IDisposable。)
- 永远不要压制无法删除目录的IOException。如果在using块内抛出异常,则尽管存在更好的诊断价值,我们也将隐藏它。
- 放弃在Dispose方法中删除目录。该类的用户必须继续负责请求删除目录。这似乎不太令人满意,因为创建该类的重要原因之一是减轻管理此资源的负担。也许有另一种方法可以提供此功能,而不会使其变得非常容易出错?
这四个选项中是否有一个明显最好的?是否有更好的方法在用户友好的API中提供此功能?
finally
块是否由于异常而运行(更不用说是什么异常了)。我认为,如果不是大多数,那也是相当多的catch
块应该是fault
块或者在finally
中使用条件代码;只有在合理地期望解决异常或者将其主动包装时,才应该捕获异常,而不是简单地进行空白的throw
。一个典型的例子:在IDisposable
对象的构造函数中发生异常应该清理对象,但不应该被“捕获”。 - supercat