結構二三事 (2)

  • 342
  • 0
  • 2019-08-14

結構型別屬性的相關問題

屬性的型別若是結構型別 (struct-type) ,當使用該屬性時也會受臨時變數影響。

在 C# 中,屬性的 gettter 會編譯出一個 method,也就是說,當我們使用 getter 去取得某個屬性值的時候,事實上它是一個 by value 的回傳值。用以下的範例說明:

 public class MyClass
 {
     public int X { get; set; }

     public void Request()
     {
         Console.WriteLine(X.ToString());
     }
 }
.method public hidebysig instance void  Request() cil managed
{
  // 程式碼大小       22 (0x16)
  .maxstack  1
  .locals init ([0] int32 V_0)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  call       instance int32 ConsoleApp2.MyClass::get_X()
  IL_0007:  stloc.0
  IL_0008:  ldloca.s   V_0
  IL_000a:  call       instance string [mscorlib]System.Int32::ToString()
  IL_000f:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0014:  nop
  IL_0015:  ret
} // end of method MyClass::Request

MyClass.Request() 在編譯後的 IL Code 會產生一個臨時變數,因為在 Request 方法內部呼叫了 X 屬性。我們稍微修改一下 MyClass 的內容,使用古早的完整屬性宣告方式,並且將 Request 方法中呼叫 X 屬性的行為改為呼叫 _x 欄位,Request 方法就不會產生臨時區域變數:

 public class MyClass
 {
     private int _x;
     public int X
     {
         get { return _x; }
         set { _x = value; }
     }

     public void Request()
     {
         Console.WriteLine(_x.ToString());
     }
 }
.method public hidebysig instance void  Request() cil managed
{
  // 程式碼大小       19 (0x13)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldflda     int32 ConsoleApp2.MyClass::_x
  IL_0007:  call       instance string [mscorlib]System.Int32::ToString()
  IL_000c:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0011:  nop
  IL_0012:  ret
} // end of method MyClass::Request

 

結語

和上一篇相同,如果屬性中使用了大型的 stuct-type,同時又會透過該屬性呼叫其執行個體成員的狀況下,搭配不破壞封裝性的原則與考慮效能的影響,如果此欄位又不是 readonly 的話,在型別內部的呼叫時可以改採用透過欄位呼叫的方式。