下面的代码,运行结果是什么?大家猜一下:

static class Program
{
static void Main(string[] args)
{
Console.WriteLine(Program.MethodC());
Program.MethodB();
Console.ReadLine();
}
static void MethodA()
{
try
{
throw new NullReferenceException();
}
catch (IndexOutOfRangeException)
{
throw;
}
finally
{
Console.WriteLine("MethodA finally");
}
}
static void MethodB()
{
try
{
MethodA();
}
catch (NullReferenceException)
{
Console.WriteLine("MethodB catch");
}
finally
{
Console.WriteLine("MethodB finally");
}
}
static int i = 1;
static int MethodC()
{
try
{
Console.WriteLine("MethodC try");
return i;
}
finally
{
i = 2;
Console.WriteLine("MethodC finally");
Console.WriteLine("MethodC:"+i);
}
}
{
static void Main(string[] args)
{
Console.WriteLine(Program.MethodC());
Program.MethodB();
Console.ReadLine();
}
static void MethodA()
{
try
{
throw new NullReferenceException();
}
catch (IndexOutOfRangeException)
{
throw;
}
finally
{
Console.WriteLine("MethodA finally");
}
}
static void MethodB()
{
try
{
MethodA();
}
catch (NullReferenceException)
{
Console.WriteLine("MethodB catch");
}
finally
{
Console.WriteLine("MethodB finally");
}
}
static int i = 1;
static int MethodC()
{
try
{
Console.WriteLine("MethodC try");
return i;
}
finally
{
i = 2;
Console.WriteLine("MethodC finally");
Console.WriteLine("MethodC:"+i);
}
}
下面给出运行结果:

MethodC try
MethodC finally
MethodC:2
1
MethodA finally
MethodB catch
MethodB finally
MethodC finally
MethodC:2
1
MethodA finally
MethodB catch
MethodB finally
看上面的运行结果,语句:Console.WriteLine(Program.MethodC())的输出为:
MethodC try
MethodC finally
MethodC:2
1
MethodC finally
MethodC:2
1
MethodC方法,主要考察的有二点,一是finally语句和return语句执行的先后顺序,二是finally语句是否可以改变return语句中返回的值。
第一点大家都知道,return前finally总是会执行,第二点就有些模糊了,运行结果也跟最初自己猜的不同。我们看一下MethodC方法生成的IL代码:
.method private hidebysig static int32 MethodC() cil managed
{
// Code size 70 (0x46)
.maxstack 2
.locals init (int32 V_0)
IL_0000: nop
.try
{
IL_0001: nop
IL_0002: ldstr "MethodC try"
IL_0007: call void [mscorlib]System.Console::WriteLine(string)
IL_000c: nop
IL_000d: ldsfld int32 ConsoleApplication.Program::i //将静态字段Program.i压入栈中
IL_0012: stloc.0 //从栈中取出值(就是刚压入的i),放到"第0号"临时变量中
IL_0013: leave.s IL_0043 //这里会退出try区块,转向IL_0043
} // end .try
finally
{
IL_0015: nop
IL_0016: ldc.i4.2 //在栈中放入一个4byte的数,值为2
IL_0017: stsfld int32 ConsoleApplication.Program::i //从栈中获取值(刚放入的2),修改i
IL_001c: ldstr "MethodC finally"
IL_0021: call void [mscorlib]System.Console::WriteLine(string)
IL_0026: nop
IL_0027: ldstr "MethodC:"
IL_002c: ldsfld int32 ConsoleApplication.Program::i
IL_0031: box [mscorlib]System.Int32
IL_0036: call string [mscorlib]System.String::Concat(object,
object)
IL_003b: call void [mscorlib]System.Console::WriteLine(string)
IL_0040: nop
IL_0041: nop
IL_0042: endfinally
} // end handler
IL_0043: nop
IL_0044: ldloc.0 //将"第0号"临时变量的值压入栈中
IL_0045: ret //退出方法,返回值
} // end of method Program::MethodC
{
// Code size 70 (0x46)
.maxstack 2
.locals init (int32 V_0)
IL_0000: nop
.try
{
IL_0001: nop
IL_0002: ldstr "MethodC try"
IL_0007: call void [mscorlib]System.Console::WriteLine(string)
IL_000c: nop
IL_000d: ldsfld int32 ConsoleApplication.Program::i //将静态字段Program.i压入栈中
IL_0012: stloc.0 //从栈中取出值(就是刚压入的i),放到"第0号"临时变量中
IL_0013: leave.s IL_0043 //这里会退出try区块,转向IL_0043
} // end .try
finally
{
IL_0015: nop
IL_0016: ldc.i4.2 //在栈中放入一个4byte的数,值为2
IL_0017: stsfld int32 ConsoleApplication.Program::i //从栈中获取值(刚放入的2),修改i
IL_001c: ldstr "MethodC finally"
IL_0021: call void [mscorlib]System.Console::WriteLine(string)
IL_0026: nop
IL_0027: ldstr "MethodC:"
IL_002c: ldsfld int32 ConsoleApplication.Program::i
IL_0031: box [mscorlib]System.Int32
IL_0036: call string [mscorlib]System.String::Concat(object,
object)
IL_003b: call void [mscorlib]System.Console::WriteLine(string)
IL_0040: nop
IL_0041: nop
IL_0042: endfinally
} // end handler
IL_0043: nop
IL_0044: ldloc.0 //将"第0号"临时变量的值压入栈中
IL_0045: ret //退出方法,返回值
} // end of method Program::MethodC
注意红色字体部分,绿色注释是我添加的,从上面IL及注释可以了解到,在MethodC方法里,会有一个隐式的”第0号“变量,来临时保存return的值,所以finally中语句虽然修改了Program.i的值,但是MethodC方法的返回值是不因finally语句而变化。
Program.MethodB(); 的输出结果,没什么好说的,大家应该都可以准确的说出来,这里只引用CLR VIA C#上一段话将try catch 语句的执行顺序简单介绍一下:
在try块中的代码(或者从try块调用的任何方法)抛出一个异常,CLR将搜索捕捉类型与抛出的异常相同(或是它的基类)的catch块。如果没有任何捕捉类型与抛出的异常匹配,CLR会去调用栈的更高一层搜索一个与异常匹配的捕捉类型。如果到了调用栈的顶部,还是没有找到具有匹配捕捉类型的一个catch块,就会发成一个未处理的异常。
一旦CLR找到一个具有匹配捕捉类型的catch块,就会执行内层所有finally块中的代码。所谓“内层finally块”是指从抛出异常的try块开始,到匹配异常的catch块之间的所有finally块。这里注意匹配异常的那个catch块所关联的finally块尚未执行,该finally块的代码一直要等到这个catch块中的代码执行完毕之后才执行。
一旦CLR找到一个具有匹配捕捉类型的catch块,就会执行内层所有finally块中的代码。所谓“内层finally块”是指从抛出异常的try块开始,到匹配异常的catch块之间的所有finally块。这里注意匹配异常的那个catch块所关联的finally块尚未执行,该finally块的代码一直要等到这个catch块中的代码执行完毕之后才执行。