本文转自 vipxiaotian(CSDN)函数
请参考下面一段简单的语句块:spa
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: }.net
你们有没有想过执行到第七步以后会出现什么结果?line7是一条return语句,按道理来讲return语句的做用就是跳出当前函数并继续调用地址的下一条语句,那么说就不执行finally,可是我想你们应该都记得finally的做用:确保有无异常的状况下finally块中的语句都能执行……因而,矛盾就这样产生鸟。。。ip
在继续下去看答案以前,亲爱的读者们,大家以为会是什么结果呢?finally会不会执行呢?请先思考一下再看答案。ci
既然在程序层看不出猫腻,那么咱们不妨深刻IL层去挖掘一下,上面代码的IL是这样子地:get
.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: retstring
请注意上面上面黄色部分代码,在catch块中,使用了leave.s这条命令来跳转到IL_0019这一行,而0019这一行正是ret命令,看起来……应该不会执行到finally才对~~难道说,从catch块就直接跳出了?!it
我想该是时候去弄明白leave.s这条命令的做用了,看看Microsoft给出的定义:io
格式 | 汇编格式 | 说明 |
---|---|---|
DE < int8 > | leave.s target | 退出受保护的代码区域(短格式)。 |
没有为此指令指定任何堆栈转换行为。table
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的