87

多年来,我一直在多种语言中使用 try-catch/except-finally 变体,今天有人问我 finally 的意义何在,我无法回答。

基本上你为什么要在 finally 中添加一个语句,而不是在整个 try-catch 块之后添加它?或者换句话说,以下代码块之间是否存在差异:

try{ //a}
catch {//b}
finally {//c}


try{//a}
catch{//b}
//c

编辑:
人们,我知道 finally 会做什么,我已经使用它很长时间了,但是我的问题是在上面的示例//c中,最后输入 finally 似乎是多余的,不是吗?

4

5 回答 5

122

块的目的finally是确保代码在三种单独使用“catch”块不能很干净地处理的情况下运行:

  1. 如果try块内的代码通过return
  2. 如果catch块内的代码要么重新抛出捕获的异常,要么——意外或有意地——最终抛出一个新异常。
  3. 如果try块中的代码遇到异常,try则没有catch.

finally可以在 every 或 throw 之前复制代码return,并将块包装catch在它们自己的 try/catch 中,以允许发生意外异常的可能性,但放弃所有这些并简单地使用finally块要容易得多。

顺便说一句,我希望语言设计者包含的一件事exception是块的参数finally,以处理需要在异常后清理但仍希望它渗透调用堆栈的情况(例如,可以将代码包装为这种构造中的构造函数,以及构造中Dispose的对象(如果构造函数将退出异常)。

于 2012-03-13T16:30:59.887 回答
2

即使在 try 块中抛出异常,也会执行 finally 块。因此,例如,如果您之前打开了一个流,您可能希望关闭该流,无论是否引发异常。finally 块对于此类问题很有用。

于 2012-03-13T16:24:24.543 回答
0

finallytry-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
  • A3myFunction是一些在 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我建议你避免这个坏习惯,并且在每个函数中只有一个返回(可能在函数的一开始就存在一些早期)。单返回函数的原因是:

  1. 代码更具可读性:你看看最后,看看函数返回了什么。在多次返回中,您必须找到所有返回出现,检查所有 if,考虑 if 何时满足,然后才知道函数返回什么。
  2. 代码可以由编译器优化,请参阅复制省略

多次返回的原因是避免了许多嵌套的 if,但还有其他技术可以解决它。Exceptions 在此规则中是例外。

于 2020-10-07T05:43:46.440 回答
0

以身作则

let v = 0;
function f() {
    try {
        v = 1;
        return 2;
    } finally {
        v = 3;
        return 4;
    }
    v = 5;
    return 6;
}

const r = f();
console.log(r, v);

以下打印“3, 4”

于 2022-01-19T10:41:06.397 回答
-1

Finally即使遇到异常,也要确保执行代码。

finally 块对于清理在 try 块中分配的任何资源以及运行任何必须执行的代码(即使存在异常)也很有用

http://msdn.microsoft.com/en-us/library/zwc8s4fz(v=vs.80).aspx

于 2012-03-13T16:19:08.867 回答