finally
是一种语法糖,允许使用DRY原理try-catch
。如果库代码没有足够的信息来处理某些状态并希望客户端代码解决该问题,通常会引发异常。如果您没有库与客户端的代码分离,则可以使用if
代替来处理所有内容try
。
让我们看看没有finally
以下情况的标准情况:
void myFunction() {
var r = allocateResources();
r.doSomething();
if(somethingBadHappens) {
freeResources(r);
throw new Exception(CODE42);
}
r.doSomethingMore();
freeResources(r);
}
在上面的代码段中,您需要重复freeResources()
:这可以是您需要重复的多个语句。这种气味和finally
阻塞是干净代码的解决方案:
void myFunction() {
var r = allocateResources();
try {
r.doSomething();
if(somethingBadHappens) throw new Exception(CODE42);
r.doSomethingMore();
}
finally {
freeResources(r);
}
happyFunction();
}
让我们实现三个抽象级别:
- A1是库代码提供
allocateResources()
功能
- A2是我们提供的代码
myFunction
,消耗A1
- A3是
myFunction
在try-catch块中消耗的一些客户端代码:
function A3code() {
try {
myFunction();
doSomething();
}
catch(Exception e) {
// no hanging resources here
Console.WriteLine(e);
}
}
现在让我们看看会发生什么:
- 如果
allocateResources()
抛出A1,我们不知道如何在A2中处理它(A2代码可以在不带控制台的环境中运行),因此我们将情况推迟到A3,而无需添加任何其他代码。如果在这里抛出Exception,则不会执行finally块,因为finally
绑定到的块try
没有输入。
- 如果
somethingBadHappens
在try块中,堆栈会退回到A3,在这种情况下,在finally
执行块之前先要处理该情况,所以如果没有异常发生,我们就不需要重复。
- 在
finally
我们添加catch
块并尝试解决A1中可能出现在调用r.doSomething
方法中的某些异常之前,通常,我们希望尽快处理异常,以使客户端代码(A3)对于客户端编码人员而言更为舒适。
happyFunction()
仅在没有任何东西抛出myFunction()
(try
块的内部或外部)时执行。
正如@supercat指出的那样,finally
如果try
块通过return退出,则该块也将执行。我建议您避免这种不良习惯,并且在每个函数中只有一个返回值(也许在函数的开始部分早就存在)。单次返回功能的原因是:
- 该代码更具可读性:您在最后看了一下该函数返回的内容。在多次返回中,您必须找到所有返回事件,检查所有if,考虑何时满足if,然后您才知道函数返回什么。
- 编译器可以优化代码,请参见copy elision。
多次返回的原因是避免了许多嵌套的ifs,但是还有其他技术可以解决它。Exception
在此规则中是例外。