一道异常处理执行顺序面试题的简单分析

异常处理,我们写的代码里经常会用到:try{}catch{}finally{}。可是大家真的了解它吗?

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

ExpandedBlockStart.gif View Code 
     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);
            }
        }

 下面给出运行结果:

ExpandedBlockStart.gif View Code 
MethodC  try
MethodC  finally
MethodC: 2
1
MethodA  finally
MethodB  catch
MethodB  finally

 看上面的运行结果,语句:Console.WriteLine(Program.MethodC())的输出为:

MethodC  try
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

  注意红色字体部分,绿色注释是我添加的,从上面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块中的代码执行完毕之后才执行。

转载于:https://www.cnblogs.com/kdalan/archive/2012/06/06/2537498.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值