C#筆記 – 屬性
-
用於以字段風格的語法設置/查詢對象的邏輯狀態,同時保證狀態不被破壞
-
靜態屬性作用於類型;實例屬性作用於對象
無參屬性
-
private sealed class PropertiesClass { int score; bool isFullCombo; public int Score { set { score = value; } } public bool IsFullCombo { get { return isFullCombo; } } public float FinalResult { get { return isFullCombo ? score * 1.5f : score; } set { return; } } } -
CLR支持靜態、實例、抽象、虛屬性
-
可用任何訪問修飾符標記
-
可以在接口定義
-
屬性都有名稱和定義,但不能重載(不能定義同名,不同類型的兩個屬性)
-
屬性包括了一到兩個方法
-
只有get:唯讀
-
只有set:只寫
-
get+set:讀寫
-
屬性操縱的字段稱為「支持字段」
-
-
取決於屬性的定義,編譯器會在程序集生成
- get || set的訪問器(根據聲明與否決定)
-
屬性定義(必然生成)


- 編譯器在屬性名之前自動附加get_ 或 set_ 前綴生成方法名
- 當編譯器發現代碼嘗試獲取/設置屬性時,會自動生成對上述方法的調用
- 屬性定義項則主要是引用了get/set方法,並可被反射(PropertyInfo)取得
AIP(自動實現屬性)
-
AIP:Automatically Implemented Property
-
public string AIP { get; set; }- 聲明了屬性,但沒有get/set的實現,C#會自動聲明一個私有字段,並自動實現get_AIP和set_AIP方法

-
AIP的好處主要是為後期的改動節省一些編譯成本
-
使用AIP就等於創建了一個屬性,以後再實現get/set方法也不需要重新編譯
-
如果使用字段,後期改做屬性,就需要重新編譯
-
-
AIP的壞處
-
AIP需在構造器中顯式初始化
-
有AIP的類型無法對其實例反序列化
-
手動實現的屬性可以打斷點,AIP不行
-
有參屬性
-
屬性的get訪問器接受一個/多個參數;set訪問器接受兩個/多個參數
-
C#對有參屬性稱作「索引器」
-
可以看成是[]操作符的重載
-
set訪問器本來就有一個隱藏參數(value)
-
類型可以提供多個重載的索引器(需要簽名不同)
-
C#不支持靜態索引器
-
-
對於CLR而言,屬性有參沒參並沒有區別,每個屬性都只是類型中定義的一對方法和元數據
-
public string this[int idx] { get { return "Hello World"; } }
-

-
-
由於索引器無法指定名稱,因此在C#中,默認為Item
-
這個默認名稱可以通過IndexerNameAttribute特性來重命名
-
該特性不會進入元數據,它只告訴編譯器要怎麼對方法和屬性的元數據命名
-
雖然可以通過這種方法來重命名索引器,但並不能用來做重載
-
-
-

初始化器初始化****公共屬性
-
無參構造器的對象,可以在實例化時,直接為其公共屬性賦值
-
Employee e1 = new Employee() { Name = "Kelvin", Age = 24 }; //或 Employee e2 = new Employee(); e2.Name = "Catherine"; e2.Age = 24; //無參構造器限定 Employee e3 = new Employee { Name = "Aden", Age = 1000 };-
構造對象 => 對公共屬性賦值
-
IL_0000: nop IL_0001: newobj instance void CLR_Ch10.Program/Employee::.ctor() IL_0006: dup IL_0007: ldstr "Kelvin" IL_000c: stfld string CLR_Ch10.Program/Employee::Name IL_0011: dup IL_0012: ldc.i4.s 24 IL_0014: stfld int32 CLR_Ch10.Program/Employee::Age IL_0019: stloc.0 IL_001a: newobj instance void CLR_Ch10.Program/Employee::.ctor() IL_001f: stloc.1 IL_0020: ldloc.1 IL_0021: ldstr "Catherine" IL_0026: stfld string CLR_Ch10.Program/Employee::Name IL_002b: ldloc.1 IL_002c: ldc.i4.s 24 IL_002e: stfld int32 CLR_Ch10.Program/Employee::Age
-
-
如果屬性實現了IEnumerable/IEnumerable
接口,就會被認為是集合,如果還重寫了Add方法,就可以直接用這種方式來初始化 -
Dictionary<string, int> table = new Dictionary<string, int> { { "Kelvin", 24 }, { "Catherine", 24 }, { "Aden", 1000 }, }; -
實際上就是由編譯器來調用Add方法(所以如果沒實現Add方法,就不可以這樣初始化)
-
IL_004f: newobj instance void class [System.Collections]System.Collections.Generic.Dictionary`2<string,int32>::.ctor() IL_0054: dup IL_0055: ldstr "Kelvin" IL_005a: ldc.i4.s 24 IL_005c: callvirt instance void class [System.Collections]System.Collections.Generic.Dictionary`2<string,int32>::Add(!0, !1) IL_0061: nop IL_0062: dup IL_0063: ldstr "Catherine" IL_0068: ldc.i4.s 24 IL_006a: callvirt instance void class [System.Collections]System.Collections.Generic.Dictionary`2<string,int32>::Add(!0, !1) IL_006f: nop IL_0070: dup IL_0071: ldstr "Aden" IL_0076: ldc.i4 0x3e8 IL_007b: callvirt instance void class [System.Collections]System.Collections.Generic.Dictionary`2<string,int32>::Add(!0, !1)
-
性能問題
-
對於簡單的get/set,JIT會將代碼內聯,避免了性能上的損失
-
內聯:將方法(get/set)直接編譯到調用它的方法中,避免在運行時發生調用所產生的開銷
-
會使編譯好的方法變大,但一般訪問器方法代碼不多
-
可使本機代碼變得輕量,執行更快
-
-
可訪問性
-
屬性本身的可訪問性限制必須小於/等於其訪問器方法(get/set)的可訪問性限制
-
一般來說,其他變量如果不明確指出訪問權限,就是默認私有的。但對於屬性而言,它的取值/賦值方法就要求明確指定訪問權限,否則就默認與屬性本身整體保持一致的訪問修飾符
屬性與字段
-
屬性不能作為out/ref參數傳給方法;字段可以
-
屬性訪問效率低於一般方法;最好是通過定義自己的訪問器方法來訪問字段
-
屬性返回引用可能並非指向對象狀態一部分/修改作用不到原始對象;字段總是在操控著原始對象
參考書目
- 《CLR via C#》(第4版) Jeffrey Richter