程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 編寫高質量代碼改善C#程序的157個建議——建議61:避免在finally內撰寫無效代碼,

編寫高質量代碼改善C#程序的157個建議——建議61:避免在finally內撰寫無效代碼,

編輯:C#入門知識

編寫高質量代碼改善C#程序的157個建議——建議61:避免在finally內撰寫無效代碼,


建議61:避免在finally內撰寫無效代碼

在闡述建議之前,需要先提出一個問題:是否存在一種打破try-finally執行順序的情況,答案是:不存在(除非應用程序本身因為某些很少出現的特殊情況在try塊中退出)。應該始終認為finally內的代碼會在方法return之前執行,哪怕return在try塊中。

正是這點,可能會讓你寫出無效的代碼,有時候,這樣的無效代碼會是一個隱藏很深的Bug。

看下面代碼:

        private static int TestIntReturnBelowFinally()
        {
            int i;
            try
            {
                i = 1;
            }
            finally
            {
                i = 2;
                Console.WriteLine("\t將int結果改為2,finally執行完畢");
            }
            return i;
        }

返回值是2。

但是:

        private static int TestIntReturnInTry()
        {
            int i;
            try
            {
                return i = 1;
            }
            finally
            {
                i = 2;
                Console.WriteLine("\t將int結果改為2,finally執行完畢");
            }
        }

返回值是1。

再看下面代碼:

        static User TestUserReturnInTry()
        {
            User user = new User() { Name = "Mike", BirthDay = new DateTime(2010, 1, 1) };
            try
            {
                return user;
            }
            finally
            {
                user.Name = "Rose";
                user.BirthDay = new DateTime(2010, 2, 2);
                Console.WriteLine("\t將user.Name改為Rose");
            }
        }

user類:

class User { public string Name { get; set; } public DateTime BirthDay { get; set; } } View Code

TestUserReturnInTry方法返回的User中,Name的值已經改為Rose了。

 

現在來解釋為什麼上面3個函數會有3種結果。查看TestIntReturnBelowFinally的finally部分的IL代碼:

  finally
  {
    IL_0004:  ldc.i4.2
    IL_0005:  stloc.0
    IL_0006:  ldstr      bytearray (09 00 06 5C 69 00 6E 00 74 00 D3 7E 9C 67 39 65   // ...\i.n.t..~.g9e
                                    3A 4E 32 00 0C FF 66 00 69 00 6E 00 61 00 6C 00   // :N2...f.i.n.a.l.
                                    6C 00 79 00 67 62 4C 88 8C 5B D5 6B )             // l.y.gbL..[.k
    IL_000b:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_0010:  endfinally
  }  // end handler
  IL_0011:  ldloc.0
  IL_0012:  ret
}

“IL_0004: ldc.i4.2”首先將2壓入棧頂

“IL_0005: stloc.0”將最頂層堆棧的值,也就是2賦值給本地變量,也就是 i (index 0)

“IL_0011: ldloc.0”將本地變量 i (index 0)的值再次壓入棧

“IL_0012: ret”結束函數,同時把棧內的返回值壓入調用者的棧中。就函數將2賦值給了返回值。

 

看方法TestIntReturnInTry()的Debug版本的IL代碼:

.method private hidebysig static int32  TestIntReturnInTry() cil managed
{
  // 代碼大小       27 (0x1b)
  .maxstack  2
  .locals init ([0] int32 i,
           [1] int32 CS$1$0000)
  IL_0000:  nop
  .try
  {
    IL_0001:  nop
    IL_0002:  ldc.i4.1
    IL_0003:  dup
    IL_0004:  stloc.0
    IL_0005:  stloc.1
    IL_0006:  leave.s    IL_0018
  }  // end .try
  finally
  {
    IL_0008:  nop
    IL_0009:  ldc.i4.2
    IL_000a:  stloc.0
    IL_000b:  ldstr      bytearray (09 00 06 5C 69 00 6E 00 74 00 D3 7E 9C 67 39 65   // ...\i.n.t..~.g9e
                                    3A 4E 32 00 0C FF 66 00 69 00 6E 00 61 00 6C 00   // :N2...f.i.n.a.l.
                                    6C 00 79 00 67 62 4C 88 8C 5B D5 6B )             // l.y.gbL..[.k
    IL_0010:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_0015:  nop
    IL_0016:  nop
    IL_0017:  endfinally
  }  // end handler
  IL_0018:  nop
  IL_0019:  ldloc.1
  IL_001a:  ret
} // end of method Program::TestIntReturnInTry

TestIntReturnInTry在IL中創建了兩個本地變量 i 和CS$1$0000 ,i 存儲的是1,然後finally中 i 被賦值為2。調用者真正得到的是由IL創建的CS$1$0000所對應的值。用Reflector查看C#代碼:

private static int TestIntReturnInTry()
{
    int i;
    int CS$1$0000;
    try
    {
        CS$1$0000 = i = 1;
    }
    finally
    {
        i = 2;
        Console.WriteLine("\t將int結果改為2,finally執行完畢");
    }
    return CS$1$0000;
}

實際上,finally中i=2沒有任何意義,所以在本函數的release版本中,IL中找不到對應的代碼:

.method private hidebysig static int32  TestIntReturnInTry() cil managed
{
  // 代碼大小       17 (0x11)
  .maxstack  1
  .locals init ([0] int32 CS$1$0000)
  .try
  {
    IL_0000:  ldc.i4.1
    IL_0001:  stloc.0
    IL_0002:  leave.s    IL_000f
  }  // end .try
  finally
  {
    IL_0004:  ldstr      bytearray (09 00 06 5C 69 00 6E 00 74 00 D3 7E 9C 67 39 65   // ...\i.n.t..~.g9e
                                    3A 4E 32 00 0C FF 66 00 69 00 6E 00 61 00 6C 00   // :N2...f.i.n.a.l.
                                    6C 00 79 00 67 62 4C 88 8C 5B D5 6B )             // l.y.gbL..[.k
    IL_0009:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_000e:  endfinally
  }  // end handler
  IL_000f:  ldloc.0
  IL_0010:  ret
} // end of method Program::TestIntReturnInTry

用Reflector查看release版本中C#代碼:

private static int TestIntReturnInTry()
{
    int CS$1$0000;
    try
    {
        CS$1$0000 = 1;
    }
    finally
    {
        Console.WriteLine("\t將int結果改為2,finally執行完畢");
    }
    return CS$1$0000;
}

 

再解釋第三個方法TestUserReturnInTry為什麼返回的是“Rose”。Reflector查看release版本中C#代碼:

private static User TestUserReturnInTry()
{
    User CS$1$0000;
    User <>g__initLocal0 = new User {
        Name = "Mike",
        BirthDay = new DateTime(0x7da, 1, 1)
    };
    User user = <>g__initLocal0;
    try
    {
        CS$1$0000 = user;
    }
    finally
    {
        user.Name = "Rose";
        user.BirthDay = new DateTime(0x7da, 2, 2);
        Console.WriteLine("\t將user.Name改為Rose");
    }
    return CS$1$0000;
}

 

User是引用類型, CS$1$0000 = user;說明CS$1$0000和user指向的是同一個對象,當在finally中 user.Name = "Rose"時CS$1$0000的Name也會變為“Rose”。所以返回的CS$1$0000的Name為“Rose”。

 

再舉一個例子:

 

        private static User TestUserReturnInTry2()
        {
            User user = new User() { Name = "Mike", BirthDay = new DateTime(2010, 1, 1) };
            try
            {
                return user;
            }
            finally
            {
                user.Name = "Rose";
                user.BirthDay = new DateTime(2010, 2, 2);
                user = null;
                Console.WriteLine("\t將user置為anull");
            }
        }

 

返回的結果不是null,而一個Name=“Rose”,BirthDay = new DateTime(2010, 2, 2)的User對象。Reflector查看release版本中C#代碼:

private static User TestUserReturnInTry2()
{
    User CS$1$0000;
    User <>g__initLocal1 = new User {
        Name = "Mike",
        BirthDay = new DateTime(0x7da, 1, 1)
    };
    User user = <>g__initLocal1;
    try
    {
        CS$1$0000 = user;
    }
    finally
    {
        user.Name = "Rose";
        user.BirthDay = new DateTime(0x7da, 2, 2);
        user = null;
        Console.WriteLine("\t將user置為anull");
    }
    return CS$1$0000;
}

 

CS$1$0000和user指向的是同一個對象,當在finally中 user=null 時,只是user指向為null了,CS$1$0000指向的對象並沒有變。

 

 

轉自:《編寫高質量代碼改善C#程序的157個建議》陸敏技

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved