引用:
Quiz:who win in finally vs return?
废话就不多讲了,直接看看下面的例子。
1)static int Test()
{
int val = 1;
try
{
return val;
}
finally
{
val = 2;
}
}
2)static int s_val;
static int Test()
{
s_val = 1;
try
{
return s_val;
}
finally
{
s_val = 2;
}
}
请问这两个例子最后的 return value 是什么?
我们仔细看看这两个了例子就知道2)与1)的不同在于前者使用静态变量代替局部变量。那么他们之间有什么不同么?
答案是他们的return value 是一样的,都是1。
便于讲解,我们拿出他们的IL代码看看:
.method private hidebysig static int32 Test() cil managed
{
// Code size 16 (0x10)
.maxstack 1
.locals init ([0] int32 val,
[1] int32 CS$1$0000)
IL_0000: nop
IL_0001: ldc.i4.1 //int val=1
IL_0002: stloc.0
.try
{
IL_0003: nop
IL_0004: ldloc.0 //变量0(val) 入栈
IL_0005: stloc.1 // 这里建个中间隐藏变量1($ret)放回栈顶的值(val)。
IL_0006: leave.s IL_000d
} // end .try
finally
{
IL_0008: nop
IL_0009: ldc.i4.2 //int val=2 并入栈
IL_000a: stloc.0 //返回栈顶的值付给变量0(val)
IL_000b: nop
IL_000c: endfinally
} // end handler
IL_000d: nop
IL_000e: ldloc.1 //取出变量1($ret)这就是放回值,但是这里我们可以看到val的值已经改变。
IL_000f: ret
} // end of method Program::Test
.method private hidebysig static int32 Test1() cil managed
{
// Code size 28 (0x1c)
.maxstack 1
.locals init ([0] int32 CS$1$0000)
IL_0000: nop
IL_0001: ldc.i4.1 // 值1入栈。
IL_0002: stsfld int32 ConsoleApp.Program::s_val //将栈顶值传给静态变量
.try
{
IL_0007: nop
IL_0008: ldsfld int32 ConsoleApp.Program::s_val //取出静态变量
IL_000d: stloc.0 //从栈顶取出的值传给局部变量0的隐藏变量(这里静态变量不是局部变量0)
IL_000e: leave.s IL_0019
} // end .try
finally
{
IL_0010: nop
IL_0011: ldc.i4.2 //同上
IL_0012: stsfld int32 ConsoleApp.Program::s_val //存储静态变量为2
IL_0017: nop
IL_0018: endfinally
} // end handler
IL_0019: nop
IL_001a: ldloc.0 //取出局部变量0的值作为返回值。
IL_001b: ret
} // end of method Program::Test1
这里无论是那种方式,都是生成一个隐藏变量存储返回值的,并不是直接返回变量的地址的。
如果大家多CLR的工作原理有所了解的话,应该不是很难理解,CLR对一个线程堆栈分配的是方法的局部变量和方法返回值。一个方法在线程堆栈中由它的实参开始到返回值结束。因此放回值是有自己的存储地址的。这样就可以解释上面的问题,看起来很像 { return i++ ;}