在
Quiz:who win in finally vs return? 中,我们看到的例子是以string作为返回值。
虽然string是引用类型,由于string的“ immutability ”特性,操作它的行为结果如同Value Type。
因此在上文中的结果只说明Value Type的显示结果,如果把返回值改为引用类型呢?
现有代码如下
class Program
{
static void Main(string[] args)
{
Console.WriteLine(Try2().Str);
}
static MyClass2 Try2()
{
MyClass2 my2 = new MyClass2();
try
{
my2.Str = "try";
return my2;
}
finally
{
my2.Str = "final";
}
}
}
class MyClass2
{
public string Str { get; set; }
}
返回结果是“final”。
看看编译器把这段Try2这个方法转成了什么样的IL?
.method private hidebysig static class TryFinally.MyClass2 Try2() cil managed
{
.maxstack 2
.locals init (
[0] class TryFinally.MyClass2 my2, //在方法里面有2个参数,1个是my2;第二个是IL生成的变量,用于返回结果
[1] class TryFinally.MyClass2 CS$1$0000)
L_0000: nop
L_0001: newobj instance void TryFinally.MyClass2::.ctor()
L_0006: stloc.0
L_0007: nop
L_0008: ldloc.0 //把第一个参数my2推出栈顶
L_0009: ldstr "try" //加载"try"字符串
L_000e: callvirt instance void TryFinally.MyClass2::set_Str(string) //调用my2的set_Str方法,即为Str属性赋值"try"。
L_0013: nop
L_0014: ldloc.0 //把第一个参数my2推出栈顶
L_0015: stloc.1 //把my2保存到CS$1$0000,即返回值
L_0016: leave.s L_0027 //转到 L_0027指令。(但注意最后一句声明.try L_0007 to L_0018 finally handler L_0018 to L_0027,因此在leave之前还需要执行L_0018 to L_0027)
L_0018: nop
L_0019: ldloc.0 //把第一个参数my2推出栈顶
L_001a: ldstr "final" //加载"final"字符串
L_001f: callvirt instance void TryFinally.MyClass2::set_Str(string) //调用my2的set_Str方法,即为Str属性赋值"final"。这里虽然没有操作CS$1$0000,但引用类型嘛,CS$1$0000的值当然影响了。
L_0024: nop
L_0025: nop
L_0026: endfinally
L_0027: nop //finally结束
L_0028: ldloc.1 //最后把CS$1$0000返回。在这里CS$1$0000的Str已经是final了
L_0029: ret
.try L_0007 to L_0018 finally handler L_0018 to L_0027
}
从上面IL代码看出,处理流程跟以前处理str是一样的,只是因为引用类型特性的原因,返回的结果就跟值类型有所不同。
另外我们还看到IL处理try finally的时候,.NET1.1和.NET2.0生成的IL有少许不同。Quiz:who win in finally vs return? 中的是.NET1.1的版本。此为.NET2.0的版本。