Try..Finally..相信自己的眼睛

问题提出

1       try 
2       { 
3         return x; 
4       }
5       finally 
6       { 
7         x = null; 
8       }

上面这段代码到底怎么执行的?

try..catch..finally 介绍

在MSDN中,try..catch..finally 的介绍如下:

  • finally 块用于清除 try 块中分配的任何资源,以及运行任何即使在发生异常时也必须执行的代码。 控制总是传递给 finally 块,与 try 块的退出方式无关。
  • catch 用于处理语句块中出现的异常,而 finally 用于保证代码语句块的执行,与前面的 try 块的退出方式无关。
  • catch 和 finally 一起使用的常见方式是:在 try 块中获取并使用资源,在 catch 块中处理异常情况,并在 finally 块中释放资源。

典型用法:

 1     void ReadFile(int index)
 2     {
 3       string path = @"c:\users\public\test.txt";
 4       char[] buffer = new char[10];
 5 
 6       StreamReader file = new StreamReader(path);
 7       try
 8       {
 9         file.ReadBlock(buffer, index, buffer.Length);
10       }
11       catch (IOException e)
12       {
13         Console.WriteLine("Error reading from {0}. Message = {1}", path, e.Message);
14       }
15       finally
16       {
17         if (file != null)
18         {
19           file.Close();
20         }
21       }
22     }

通常 finally 中的代码只负责清理资源。

那么,如果 finally 中包含业务逻辑,try..finally..的执行顺序会对业务逻辑有怎样的影响呢?

try..finally 的执行顺序

回到问题,

    void Main()
    {
      Console.WriteLine(TestTryFinally());
    }

    public string TestTryFinally()
    {
      string x = "init";
      try
      {
        x = "try";
        return x;
      }
      finally
      {
        x = "finally";
      }
    }

这里的执行顺序是:

  1. 执行 return 之前的代码
  2. 对 return 语句求值
  3. 执行 finally 中的代码
  4. 在第 2 步中的求值结果被返回

所以,具体是否对返回值有影响,得看 x 变量的类型。如果是不可变类型,则 finally 中的代码对 return 的求值结果没有任何影响。而如果是可变类型,则 finally 中的代码会改变 return 求值结果的内容。

上述代码,在 x 类型为 string 时,返回值为 "try"。

查看 IL 代码,

 1 IL_0000:  ldarg.0     
 2 IL_0001:  call        UserQuery.TestTryFinally
 3 IL_0006:  call        System.Console.WriteLine
 4 
 5 TestTryFinally:
 6 IL_0000:  ldstr       "init"
 7 IL_0005:  stloc.0     // x
 8 IL_0006:  ldstr       "try"
 9 IL_000B:  stloc.0     // x
10 IL_000C:  ldloc.0     // x
11 IL_000D:  stloc.1     // CS$1$0000
12 IL_000E:  leave.s     IL_0017
13 IL_0010:  ldstr       "finally"
14 IL_0015:  stloc.0     // x
15 IL_0016:  endfinally  
16 IL_0017:  ldloc.1     // CS$1$0000
17 IL_0018:  ret    

发现在 stloc.1 处会创建 CS$1$0000 临时变量来存储 return 返回值。

从程序集反编译代码查看结果,程序已经被优化。

 1 // ConsoleApplication11_TryFinallyTest.Program
 2 public string TestTryFinally()
 3 {
 4     string result;
 5     try
 6     {
 7         string x = "try";
 8         result = x;
 9     }
10     finally
11     {
12     }
13     return result;
14 }

更多测试结果

  1 using System;
  2 using System.Text;
  3 
  4 namespace ConsoleApplication11_TryFinallyTest
  5 {
  6   class Program
  7   {
  8     static void Main(string[] args)
  9     {
 10       Console.WriteLine(MethodA());
 11       Console.WriteLine(a);
 12 
 13       Console.WriteLine(MethodB().ToString());
 14       Console.WriteLine(b.ToString());
 15 
 16       Console.WriteLine(MethodC());
 17       Console.WriteLine(c);
 18 
 19       Console.WriteLine(MethodD().ToString());
 20       Console.WriteLine(d.ToString());
 21 
 22       Console.WriteLine(MethodE().ToString());
 23       Console.WriteLine(e.ToString());
 24 
 25       Console.ReadKey();
 26     }
 27 
 28     static string a;
 29     static string MethodA()
 30     {
 31       try
 32       {
 33         a = "tryA";
 34         return a;
 35       }
 36       finally
 37       {
 38         a = "finallyA";
 39       }
 40     }
 41 
 42     static StringBuilder b = new StringBuilder();
 43     static StringBuilder MethodB()
 44     {
 45       try
 46       {
 47         b.Append("tryB");
 48         return b;
 49       }
 50       finally
 51       {
 52         b.Append("finallyB");
 53       }
 54     }
 55 
 56     static int c;
 57     static int MethodC()
 58     {
 59       try
 60       {
 61         c = 3;
 62         return c;
 63       }
 64       finally
 65       {
 66         c = 4;
 67       }
 68     }
 69 
 70     static Person d;
 71     static Person MethodD()
 72     {
 73       try
 74       {
 75         d = new Person() { Name = "tryD" };
 76         return d;
 77       }
 78       finally
 79       {
 80         d = new Person() { Name = "finallyD" };
 81       }
 82     }
 83 
 84     static Person e;
 85     static Person MethodE()
 86     {
 87       e = new Person() { Name = "E" };
 88       try
 89       {
 90         e.Name = "tryE";
 91         return e;
 92       }
 93       finally
 94       {
 95         e.Name = "finallyE";
 96       }
 97     }
 98 
 99     class Person
100     {
101       public string Name { get; set; }
102       public override string ToString()
103       {
104         return Name;
105       }
106     }
107   }
108 }
View Code

参考资料

 

posted @ 2013-10-13 16:08  sangmado  阅读(1126)  评论(4编辑  收藏  举报