Exception handling try-catch/except-finally语句中finally的意义是什么

Exception handling try-catch/except-finally语句中finally的意义是什么,exception-handling,finally,Exception Handling,Finally,多年来,我在许多语言中使用了try-catch/except-finally变体,今天有人问我finally的意义是什么,我无法回答 基本上,你为什么要把一个语句放在最后,而不是放在整个try-catch块之后?或者换句话说,以下代码块之间是否存在差异: try{ //a} catch {//b} finally {//c} try{//a} catch{//b} //c 编辑: 各位,我知道最终会做什么,我已经使用它很多年了,但我的问题是在上面的示例中,将/c放入最终似乎是多余的,不是吗

多年来,我在许多语言中使用了try-catch/except-finally变体,今天有人问我finally的意义是什么,我无法回答

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

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


try{//a}
catch{//b}
//c
编辑:

各位,我知道最终会做什么,我已经使用它很多年了,但我的问题是在上面的示例中,将
/c
放入最终似乎是多余的,不是吗?

最后
确保即使出现异常,也执行您的代码

finally块用于清理try块中分配的任何资源,以及运行即使出现异常也必须执行的任何代码


即使try块中抛出异常,也会执行Finally块。因此,例如,如果您以前打开过一个流,您可能希望关闭该流,或者抛出异常,或者不抛出异常。Finally块对于此类问题非常有用。

Finally块的目的是确保代码在三种情况下运行,这三种情况单独使用“catch”块不会非常干净地处理:

  • 如果
    try
    块中的代码通过
    return退出
    
  • 如果
    catch
    块中的代码重新调用捕获的异常,或者(意外地或有意地)最终抛出一个新异常。
  • 如果
    try
    块中的代码遇到异常,
    try
    没有
    catch
    。 人们可以在每次
    返回或抛出之前复制
    finally
    代码,并将
    catch
    块包装在自己的try/catch中,以允许意外异常发生的可能性,但放弃所有这些,只需使用
    finally
    块就容易多了


    顺便说一句,我希望语言设计人员能够包括一个
    异常
    参数到
    finally
    块,以处理在异常发生后需要清理,但仍然希望它渗透调用堆栈的情况(例如,可以将构造函数的代码包装在这样的构造中,如果构造函数将异常退出,则
    处理
    正在构造的对象)
    试试看

    让我们看看没有
    的标准情况最后

    void myFunction() {
      var r = allocateResources();
      r.doSomething();
      if(somethingBadHappens) {
        freeResources(r);
        throw new Exception(CODE42);
      }
      r.doSomethingMore();
      freeResources(r);
    }
    
    在上面的代码段中,您重复了
    freeResources()
    :这可能是多条需要重复的语句。这是一种气味,
    最后
    块是干净代码的解决方案:

    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是某个客户端代码在try-catch块中使用
      myFunction
    现在让我们看看会发生什么:

    • 如果
      allocateResources()
      抛出A1,我们不知道如何在A2中处理它(A2代码可以在无控制台的环境中运行),因此我们将情况延迟到A3,而不添加任何其他代码。如果在此处引发异常,则不会执行finally块,因为
      finally
      绑定到未输入的
      try
    • 如果在try块中发生
      somethingbad
      ,堆栈将展开到A3,在A3中处理情况,但在执行
      最后
      块之前,因此如果没有异常发生,我们不需要重复它
    • 最后
      之前,我们可以添加
      catch
      块,并尝试解决A1中的一些潜在异常,这些异常可能出现在调用
      r.doSomething
      方法中。通常我们希望尽快处理异常,以使客户端代码(A3)更适合客户端编码人员
    • happyFunction()
      只有在
      myFunction()
      中没有抛出任何内容时才会执行(在
      try
      块的内部或外部)
    正如@supercat所指出的,如果
    try
    块通过return退出,那么
    finally
    块也会执行。我建议您避免这种坏习惯,每个函数只有一个return(可能在函数的最开始就存在一些早期返回)。使用单返回函数的原因如下:

  • 代码更具可读性:您可以查看结尾并查看函数返回的内容。在多次返回中,您必须找到所有返回事件,检查所有ifs,思考ifs满足的时间,然后才知道函数返回的内容
  • 编译器可以优化代码,请参阅

  • 多重返回的原因是避免了许多嵌套的ifs,但还有其他技术可以解决它。
    异常
    s在此规则中是异常。

    但是我提到的两个代码块之间的区别是什么?在这两种情况下都执行了
    //c
    ,对吗?请查看编辑后的问题。@Ali-finally块允许您如果您不在本地处理,请清理分配的所有资源,并允许expetion对调用堆栈进行propogate备份。在第二个版本中,如果//a中的代码调用“return”退出函数,则不会执行//c。最后确保调用//c,即使函数提前退出……也不会回答实际问题我们试过了,你抓住了,我们终于找到了这个问题的答案!我认为这里的关键是,即使有
    返回
    function A3code() {
      try {
        myFunction();
        doSomething();
      }
      catch(Exception e) {
        // no hanging resources here
        Console.WriteLine(e);
      }
    }