異常處理,我們寫的代碼裏經常會用到:try{}catch{}finally{}。可是大家真的瞭解它嗎?
下面的代碼,運行結果是什麼?大家猜一下:
{
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 finally
MethodC:2
1
MethodA finally
MethodB catch
MethodB finally
看上面的運行結果,語句:Console.WriteLine(Program.MethodC())的輸出爲:
MethodC finally
MethodC:2
1
MethodC方法,主要考察的有二點,一是finally語句和return語句執行的先後順序,二是finally語句是否可以改變return語句中返回的值。
第一點大家都知道,return前finally總是會執行,第二點就有些模糊了,運行結果也跟最初自己猜的不同。我們看一下MethodC方法生成的IL代碼:
{
// 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語句而變化。
一旦CLR找到一個具有匹配捕捉類型的catch塊,就會執行內層所有finally塊中的代碼。所謂“內層finally塊”是指從拋出異常的try塊開始,到匹配異常的catch塊之間的所有finally塊。這裏注意匹配異常的那個catch塊所關聯的finally塊尚未執行,該finally塊的代碼一直要等到這個catch塊中的代碼執行完畢之後才執行。