Answers:
对一般做法有什么想法?
是。要小心。当您的finally块正在运行时,它完全有可能在运行,因为引发了未处理的意外异常。这意味着某些东西已损坏,并且可能发生完全意外的事情。
在这种情况下,可以说根本不应该在finally块中运行代码。最终代码块中的代码可能构建为假设它依赖的子系统是健康的,而实际上它们可能会被严重破坏。finally块中的代码可能会使情况更糟。
例如,我经常看到这种情况:
DisableAccessToTheResource();
try
{
DoSomethingToTheResource();
}
finally
{
EnableAccessToTheResource();
}
该代码的作者正在思考“我正在暂时改变世界状况;我需要将状态恢复到被召唤之前的状态”。但是,让我们考虑一下所有可能出错的方法。
首先,调用者可能已禁用对资源的访问;在这种情况下,此代码可能会过早重新启用它。
其次,如果DoSomethingToTheResource引发异常,那么启用访问资源的正确做法是正确的???管理资源的代码意外中断。该代码实际上是“如果管理代码已损坏,请确保其他代码可以尽快调用该已损坏的代码,以使它也可能严重失败 ”。这似乎是个坏主意。
第三,如果DoSomethingToTheResource引发异常,那么我们如何知道EnableAccessToTheResource也不会引发异常?无论多么糟糕,使用资源都可能会影响清除代码,在这种情况下,原始的异常将丢失并且问题将更难以诊断。
我倾向于不使用try-finally块来编写这样的代码:
bool wasDisabled = IsAccessDisabled();
if (!wasDisabled)
DisableAccessToTheResource();
DoSomethingToTheResource();
if (!wasDisabled)
EnableAccessToTheResource();
现在除非状态需要改变,否则状态不会改变。现在,调用者的状态不再混乱。现在,如果DoSomethingToTheResource失败,那么我们将不会重新启用访问。我们假设某些东西已被严重破坏,并且不冒险通过尝试保持运行代码来使情况恶化。如果可以的话,让呼叫者解决问题。
那么什么时候运行finally块是个好主意?首先,在预期到异常时。例如,您可能希望锁定文件的尝试可能会失败,因为其他人已将其锁定。在这种情况下,捕获异常并将其报告给用户是有意义的。在这种情况下,减少了关于破坏的不确定性;您不太可能通过清理来使事情变得更糟。
其次,当您要清理的资源是稀缺的系统资源时。例如,在finally块中关闭文件句柄很有意义。(“使用”当然是编写try-finally块的另一种方式。)文件的内容可能已损坏,但是您现在无法执行任何操作。文件句柄最终将被关闭,因此它最好尽快而不是稍后。