一道异常处理执行顺序面试题的简单分析
生活随笔
收集整理的這篇文章主要介紹了
一道异常处理执行顺序面试题的简单分析
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
異常處理,我們寫的代碼里經(jīng)常會(huì)用到: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
MethodC?finally
MethodC:2
1
{
??//?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 ?//將靜態(tài)字段Program.i壓入棧中
????IL_0012:??stloc.0 ?//從棧中取出值(就是剛壓入的i),放到"第0號(hào)"臨時(shí)變量中
????IL_0013:??leave.s????IL_0043 ?//這里會(huì)退出try區(qū)塊,轉(zhuǎn)向IL_0043
??}??//?end?.try
??finally
??{
????IL_0015:??nop
????IL_0016:??ldc.i4.2 ?//在棧中放入一個(gè)4byte的數(shù),值為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號(hào)"臨時(shí)變量的值壓入棧中
??IL_0045:??ret ?//退出方法,返回值
}?//?end?of?method?Program::MethodC
在try塊中的代碼(或者從try塊調(diào)用的任何方法)拋出一個(gè)異常,CLR將搜索捕捉類型與拋出的異常相同(或是它的基類)的catch塊。如果沒有任何捕捉類型與拋出的異常匹配,CLR會(huì)去調(diào)用棧的更高一層搜索一個(gè)與異常匹配的捕捉類型。如果到了調(diào)用棧的頂部,還是沒有找到具有匹配捕捉類型的一個(gè)catch塊,就會(huì)發(fā)成一個(gè)未處理的異常。
一旦CLR找到一個(gè)具有匹配捕捉類型的catch塊,就會(huì)執(zhí)行內(nèi)層所有finally塊中的代碼。所謂“內(nèi)層finally塊”是指從拋出異常的try塊開始,到匹配異常的catch塊之間的所有finally塊。這里注意匹配異常的那個(gè)catch塊所關(guān)聯(lián)的finally塊尚未執(zhí)行,該finally塊的代碼一直要等到這個(gè)catch塊中的代碼執(zhí)行完畢之后才執(zhí)行。
下面的代碼,運(yùn)行結(jié)果是什么?大家猜一下:
????{
????????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);
????????????}
????????}
?下面給出運(yùn)行結(jié)果:
View Code? MethodC?tryMethodC?finally
MethodC:2
1
MethodA?finally
MethodB?catch
MethodB?finally
?看上面的運(yùn)行結(jié)果,語句:Console.WriteLine(Program.MethodC())的輸出為:
MethodC?finally
MethodC:2
1
MethodC方法,主要考察的有二點(diǎn),一是finally語句和return語句執(zhí)行的先后順序,二是finally語句是否可以改變r(jià)eturn語句中返回的值。
第一點(diǎn)大家都知道,return前finally總是會(huì)執(zhí)行,第二點(diǎn)就有些模糊了,運(yùn)行結(jié)果也跟最初自己猜的不同。我們看一下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 ?//將靜態(tài)字段Program.i壓入棧中
????IL_0012:??stloc.0 ?//從棧中取出值(就是剛壓入的i),放到"第0號(hào)"臨時(shí)變量中
????IL_0013:??leave.s????IL_0043 ?//這里會(huì)退出try區(qū)塊,轉(zhuǎn)向IL_0043
??}??//?end?.try
??finally
??{
????IL_0015:??nop
????IL_0016:??ldc.i4.2 ?//在棧中放入一個(gè)4byte的數(shù),值為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號(hào)"臨時(shí)變量的值壓入棧中
??IL_0045:??ret ?//退出方法,返回值
}?//?end?of?method?Program::MethodC
注意紅色字體部分,綠色注釋是我添加的,從上面IL及注釋可以了解到,在MethodC方法里,會(huì)有一個(gè)隱式的”第0號(hào)“變量,來臨時(shí)保存return的值,所以finally中語句雖然修改了Program.i的值,但是MethodC方法的返回值是不因finally語句而變化。
Program.MethodB();?的輸出結(jié)果,沒什么好說的,大家應(yīng)該都可以準(zhǔn)確的說出來,這里只引用CLR VIA C#上一段話將try catch 語句的執(zhí)行順序簡單介紹一下:在try塊中的代碼(或者從try塊調(diào)用的任何方法)拋出一個(gè)異常,CLR將搜索捕捉類型與拋出的異常相同(或是它的基類)的catch塊。如果沒有任何捕捉類型與拋出的異常匹配,CLR會(huì)去調(diào)用棧的更高一層搜索一個(gè)與異常匹配的捕捉類型。如果到了調(diào)用棧的頂部,還是沒有找到具有匹配捕捉類型的一個(gè)catch塊,就會(huì)發(fā)成一個(gè)未處理的異常。
一旦CLR找到一個(gè)具有匹配捕捉類型的catch塊,就會(huì)執(zhí)行內(nèi)層所有finally塊中的代碼。所謂“內(nèi)層finally塊”是指從拋出異常的try塊開始,到匹配異常的catch塊之間的所有finally塊。這里注意匹配異常的那個(gè)catch塊所關(guān)聯(lián)的finally塊尚未執(zhí)行,該finally塊的代碼一直要等到這個(gè)catch塊中的代碼執(zhí)行完畢之后才執(zhí)行。
轉(zhuǎn)載于:https://www.cnblogs.com/kdalan/archive/2012/06/06/2537498.html
總結(jié)
以上是生活随笔為你收集整理的一道异常处理执行顺序面试题的简单分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关于 uniqueidentifier
- 下一篇: 线性表的表示和实现方式之链式表示和实现