finally
try-catch
是在模式中允许 DRY 原则的语法糖。如果库代码没有足够的信息来处理某些状态并希望客户端代码解决它,通常会引发异常。如果您没有库-客户端代码分离,则可以通过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,而不添加任何进一步的代码。如果此处抛出异常,则不会执行 finally 块,因为finally
绑定到try
未输入的。
- 如果
somethingBadHappens
在 try 块中,堆栈展开到 A3,在此情况下已处理但在finally
执行块之前,因此如果没有发生异常,我们不需要重复它。
- 在
finally
我们可以添加catch
块并尝试解决 A1 中可能出现在调用r.doSomething
方法中的一些潜在异常之前。通常我们希望尽快处理异常,以使客户端代码 (A3) 更适合客户端编码人员。
happyFunction()
myFunction()
只有在没有任何东西抛出(块内部或外部)时才执行try
。
正如@supercat 指出的那样,如果块通过返回退出,该finally
块也会执行。try
我建议你避免这个坏习惯,并且在每个函数中只有一个返回(可能在函数的一开始就存在一些早期)。单返回函数的原因是:
- 代码更具可读性:你看看最后,看看函数返回了什么。在多次返回中,您必须找到所有返回出现,检查所有 if,考虑 if 何时满足,然后才知道函数返回什么。
- 代码可以由编译器优化,请参阅复制省略。
多次返回的原因是避免了许多嵌套的 if,但还有其他技术可以解决它。Exception
s 在此规则中是例外。