C#筆記 – 字段
字段
-
readonly int someReadonly = 2; static int someStaticField = 3; -
//Check in ildasm someReadonly : private initonly int32 someStaticField : private static int32 -
字段是一個值類型的實例/引用類型的引用,一般聲明為私有
-
CLR支持類型(靜態)字段和實例(非靜態)字段
- 靜態時,為類型狀態的一部分;非靜態時,為對象狀態的一部分
- 靜態字段的動態內存
- 類型是在引用了它的任何方法首次進行JIT編譯時,加載到一個AppDomain裡的
- 類型加載到AppDomain後,創建類型對象
- 容納靜態字段數據所需的動態內存是在類型對象中分配的
- 而字段的值則在運行時才能獲取
- 實例字段的動態內存
- 在構造類型實例時分配
字段修飾符
-
CLR C# Desc Static static 類型狀態一部分 Instance 默認 與類型的一個實例關聯 InitOnly readonly 只能由一個構造器中的代碼寫入 Volatile volatile compiler、CLR、硬件不會對訪問這種字段的代碼執行「線程不安全」的優化措施 -
CLR支持readonly和read/write字段
-
readonly只能在構造時初始化
-
編譯器和驗證機制會確保readonly字段不會被構造器以外的任何方法寫入
- 可以利用反射來修改readonly字段
-
如果readonly的字段是引用類型,其不可改變之處在於其引用,而不是它引用的對象,以readonly數組為例
-
public sealed class AType { public static readonly char[] charArr = new char[] { 'A', 'B', 'C' }; } public sealed class BType { public static void Modify() { //有效且編譯通過 AType.charArr[0] = 'X'; AType.charArr[1] = 'Y'; AType.charArr[2] = 'Z'; //無效且無法通過編譯 //AType.charArr = null; } }
-
-
-
public static readonly VS const
- 由於字段只有在運行時才能獲取,因此引用了定義字段的exe在編譯時,並不會像常量一樣,把字段的值嵌入到IL裡面
-
所以,exe調用字段時,需要加載定義字段的dll,並獲取對應的字段值
-
這使得只要重新打包dll,就可以修改exe加載的字段值;而不需要像const一樣,還要重新打包exe
-
首次打包DLL和EXE
-
//DLL public sealed class SomeLibraryType { public static readonly string ReadOnlyString = "Orange"; } -
//EXE public sealed class Program { public static void Main() { Console.WriteLine("Readonly String: " + SomeLibraryType.ReadOnlyString); Console.ReadLine(); } } -
IL_001d: nop IL_001e: ldstr "Readonly String: " IL_0023: ldsfld string [Program]CLR_Ch7.SomeLibraryType::ReadOnlyString //加載定義了字段的程序集 IL_0028: call string [mscorlib]System.String::Concat(string, string) IL_002d: call void [mscorlib]System.Console::WriteLine(string) IL_0032: nop IL_0033: call string [mscorlib]System.Console::ReadLine() IL_0038: pop -
輸出”Orange”
-

-
-
修改字段的值後,只重新打包DLL
-
//DLL public sealed class SomeLibraryType { public static readonly string ReadOnlyString = "Apple"; } -

-
-
同時readonly又確保了字段的不變性,因此,public static readonly才多被用作const的「彈性替代品」
-
- 由於字段只有在運行時才能獲取,因此引用了定義字段的exe在編譯時,並不會像常量一樣,把字段的值嵌入到IL裡面
內聯初始化字段
-
字段除可以在構造器裡面初始化外,還可以像下面這樣進行內聯初始化
-
public sealed class Program { //內聯初始化 public readonly int inLineVar = 100; //構造器初始化 public readonly int constructorVar; public Program() { constructorVar = 500; } }
-
-
但內聯初始化在C#底層也是在構造器對字段進行初始化的,內聯初始化只是一種語法糖
-
另外,使用內聯語法還有一些性能問題
- 在引用類型中:
-
C#
-
internal sealed class SomeType { int m_X = 5; string m_S = "Test"; byte m_B; public SomeType() { } public SomeType(int x) { m_X = x; } public SomeType(string s) { m_S = s; } }
-
-
IL
-
SomeType()
-
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // 程式碼大小 27 (0x1b) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldc.i4.5 IL_0002: stfld int32 CLR_Ch8.SomeType::m_X IL_0007: ldarg.0 IL_0008: ldstr "Test" IL_000d: stfld string CLR_Ch8.SomeType::m_S IL_0012: ldarg.0 IL_0013: call instance void [System.Runtime]System.Object::.ctor() IL_0018: nop IL_0019: nop IL_001a: ret } // end of method SomeType::.ctor
-
-
SomeType(int x)
-
.method public hidebysig specialname rtspecialname instance void .ctor(int32 x) cil managed { // 程式碼大小 34 (0x22) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldc.i4.5 IL_0002: stfld int32 CLR_Ch8.SomeType::m_X IL_0007: ldarg.0 IL_0008: ldstr "Test" IL_000d: stfld string CLR_Ch8.SomeType::m_S IL_0012: ldarg.0 IL_0013: call instance void [System.Runtime]System.Object::.ctor() IL_0018: nop IL_0019: nop IL_001a: ldarg.0 IL_001b: ldarg.1 IL_001c: stfld int32 CLR_Ch8.SomeType::m_X IL_0021: ret } // end of method SomeType::.ctor
-
-
SomeType(string s)
-
.method public hidebysig specialname rtspecialname instance void .ctor(string s) cil managed { // 程式碼大小 34 (0x22) .maxstack 8 IL_0000: ldarg.0 IL_0001: ldc.i4.5 IL_0002: stfld int32 CLR_Ch8.SomeType::m_X IL_0007: ldarg.0 IL_0008: ldstr "Test" IL_000d: stfld string CLR_Ch8.SomeType::m_S IL_0012: ldarg.0 IL_0013: call instance void [System.Runtime]System.Object::.ctor() IL_0018: nop IL_0019: nop IL_001a: ldarg.0 IL_001b: ldarg.1 IL_001c: stfld string CLR_Ch8.SomeType::m_S IL_0021: ret } // end of method SomeType::.ctor
-
-
簡單來說,使用「內聯」語化來初始化字段,C#編譯器會將這些字段轉換成構造器中的代碼來進行初始化
- 而這種轉換會在定義的「所有」構造器中發生,因此,在上例中有3個構造器的話,每一個構造器都會生成「內聯初始化字段轉換至構造器內初始化」的IL代碼
- 而在完成這些初始化後,會插入對基類實例構造器的調用
- 最後才是自身的實例構造器的代碼
-
-
一種優化的手段是創建一個構造器來執行所有字段的初始化,然後讓其他構造器顯式調用(使用this關鍵字)該構造器,從而減少生成的代碼:
-
C#
-
internal sealed class SomeTypeOptimized { int m_x; string m_s; byte m_b; public SomeTypeOptimized() { m_x = 10; m_s = "Test"; m_b = 0; } public SomeTypeOptimized(int x) : this() { m_x = x; } public SomeTypeOptimized(string s) : this() { m_s = s; } public SomeTypeOptimized(int x, string s) : this() { m_x = x; m_s = s; } }
-
-
IL
-
SomeTypeOptimized()
-
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // 程式碼大小 35 (0x23) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [System.Runtime]System.Object::.ctor() IL_0006: nop IL_0007: nop IL_0008: ldarg.0 IL_0009: ldc.i4.s 10 IL_000b: stfld int32 CLR_Ch8.SomeTypeOptimized::m_x IL_0010: ldarg.0 IL_0011: ldstr "Test" IL_0016: stfld string CLR_Ch8.SomeTypeOptimized::m_s IL_001b: ldarg.0 IL_001c: ldc.i4.0 IL_001d: stfld uint8 CLR_Ch8.SomeTypeOptimized::m_b IL_0022: ret } // end of method SomeTypeOptimized::.ctor
-
-
SomeTypeOptimized(int x) : this()
-
.method public hidebysig specialname rtspecialname instance void .ctor(int32 x) cil managed { // 程式碼大小 16 (0x10) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void CLR_Ch8.SomeTypeOptimized::.ctor() IL_0006: nop IL_0007: nop IL_0008: ldarg.0 IL_0009: ldarg.1 IL_000a: stfld int32 CLR_Ch8.SomeTypeOptimized::m_x IL_000f: ret } // end of method SomeTypeOptimized::.ctor
-
-
SomeTypeOptimized(string s) : this()
-
.method public hidebysig specialname rtspecialname instance void .ctor(string s) cil managed { // 程式碼大小 16 (0x10) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void CLR_Ch8.SomeTypeOptimized::.ctor() IL_0006: nop IL_0007: nop IL_0008: ldarg.0 IL_0009: ldarg.1 IL_000a: stfld string CLR_Ch8.SomeTypeOptimized::m_s IL_000f: ret } // end of method SomeTypeOptimized::.ctor
-
-
SomeTypeOptimized(int x, string s) : this()
-
.method public hidebysig specialname rtspecialname instance void .ctor(int32 x, string s) cil managed { // 程式碼大小 23 (0x17) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void CLR_Ch8.SomeTypeOptimized::.ctor() IL_0006: nop IL_0007: nop IL_0008: ldarg.0 IL_0009: ldarg.1 IL_000a: stfld int32 CLR_Ch8.SomeTypeOptimized::m_x IL_000f: ldarg.0 IL_0010: ldarg.2 IL_0011: stfld string CLR_Ch8.SomeTypeOptimized::m_s IL_0016: ret } // end of method SomeTypeOptimized::.ctor
-
-
-
-
- 在引用類型中:
參考書目
- 《CLR via C#》(第4版) Jeffrey Richter