文章出處

問題提出

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

參考資料

 


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()