結構型別屬性的相關問題
屬性的型別若是結構型別 (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 的話,在型別內部的呼叫時可以改採用透過欄位呼叫的方式。