在catch中return,会执行finally吗?

    技术2022-05-12  42

    请参考下面一段简单的语句块:

    1:  try 2:  { 3:      throw new Exception("new exception"); 4:  } 5:  catch(Exception ex) 6:  { 7:      return; 8:  } 9:  finally 10:  { 11:      Console.WriteLine("a"); 12:  }

           大家有没有想过执行到第七步之后会出现什么结果?line7是一条return语句,按道理来说return语句的作用就是跳出当前函数并继续调用地址的下一条语句,那么说就不执行finally,但是我想大家应该都记得finally的作用:确保有无异常的情况下finally块中的语句都能执行……于是,矛盾就这样产生鸟。。。

          在继续下去看答案之前,亲爱的读者们,你们觉得会是什么结果呢?finally会不会执行呢?请先思考一下再看答案。

           既然在程序层看不出猫腻,那么我们不妨深入IL层去挖掘一下,上面代码的IL是这样子地:

      .try   {     .try     {       IL_0000:  ldstr      "new exception"       IL_0005:  newobj     instance void [mscorlib]System.Exception::.ctor(string)       IL_000a:  throw     }  // end .try     catch [mscorlib]System.Exception     {       IL_000b:  stloc.0       IL_000c:  leave.s    IL_0019     }  // end handler   }  // end .try   finally   {     IL_000e:  ldstr      "a"     IL_0013:  call       void [mscorlib]System.Console::WriteLine(string)     IL_0018:  endfinally   }  // end handler   IL_0019:  ret

          请注意上面上面黄色部分代码,在catch块中,使用了leave.s这条命令来跳转到IL_0019这一行,而0019这一行正是ret命令,看起来……应该不会执行到finally才对~~难道说,从catch块就直接跳出了?!

          我想该是时候去弄明白leave.s这条命令的作用了,看看Microsoft给出的定义:

    格式汇编格式说明DE < int8 >leave.s target退出受保护的代码区域(短格式)。

    没有为此指令指定任何堆栈转换行为。

    leave.s 指令无条件将控制转移到传递的目标指令,这表示为距当前指令之后的指令的开始处的 1 字节有符号偏移量。

    leave.s 指令类似于 br 指令,但它可用于退出 try、filter 或 catch 块,而一般分支指令只能在此类块中使用以在其内部转移控制。leave.s 指令清空计算堆栈并确保执行周围适当的 finally 块。

    不能使用 leave.s 指令退出 finally 块。为了简化异常处理程序的代码生成,一个有效的方法是在 Catch 块的内部使用 leave.s 指令将控制转移到关联的 try 块中的任何指令。

    如果指令有一个或多个前缀代码,则只能将控制转移到其中的第一个前缀。

          OK,至此,一切明了了~~leave.s会确保finally块的执行,所以,即使在catch中使用了return语句想直接跳出,还是会先执行finally再return的。


    最新回复(0)