為什麼需要屬性
屬性(Property)本質上是用來包裝資料存取的一層語法。它讓呼叫端看起來像在讀寫欄位,但實際上背後執行的是方法。
這樣做的好處是:
- 可以保留欄位風格的使用方式
- 又能在讀寫時加入驗證、轉換或計算
- 比直接公開欄位更容易維持封裝
靜態屬性作用在型別上,實例屬性作用在物件上。
最基本的屬性
private sealed class PropertiesClass
{
private int _score;
private bool _isFullCombo;
public int Score
{
set { _score = value; }
}
public bool IsFullCombo
{
get { return _isFullCombo; }
}
public float FinalResult
{
get { return _isFullCombo ? _score * 1.5f : _score; }
}
}
從語意上來看,屬性可以是:
- 唯讀:只有
get - 唯寫:只有
set - 讀寫:同時有
get/set
CLR 怎麼看屬性
CLR 並不直接把屬性當成「特殊欄位」。對它來說,屬性是一組 accessor 方法外加一筆 metadata。
也就是說,一個屬性最終通常會對應成:
get_PropertyName()set_PropertyName(...)- property metadata


當 C# 編譯器看到:
obj.Score = 100;
bool fc = obj.IsFullCombo;
它實際上會翻譯成對 set_Score(...) 與 get_IsFullCombo() 的呼叫。
因此屬性的重點從來不是「少打一點字」,而是用欄位風格的語法,安全地包裝方法呼叫。
自動實作屬性
最常見的簡寫是 AIP(Automatically Implemented Property):
public string Name { get; set; }
這代表你把 accessor 的實作交給編譯器,編譯器會幫你生成:
- 一個私有 backing field
- 對應的
get_Name()/set_Name(...)

這種寫法特別適合:
- 目前只是單純資料承載
- 暫時不需要驗證邏輯
- 想保留未來再改成手寫屬性的空間
自動屬性的優缺點
優點
- 寫法短
- 可讀性高
- 後續若要補上邏輯,從呼叫端看起來不需要更改 API 形式
缺點
- 沒辦法直接插入自訂的讀寫流程
- 不能像手寫屬性那樣細緻控制 backing field 行為
- 有些初始化與序列化情境下,需要更明確的建模方式
所以 AIP 適合「現在很單純」的資料,但不代表它一定是所有屬性的終點。
索引器
帶參數的屬性,在 C# 中通常叫索引器(Indexer)。
public string this[int index]
{
get { return "Hello World"; }
}
對呼叫端來說,它看起來像是在用 [] 操作物件;對 CLR 來說,它仍然只是屬性的另一種形式。

預設名稱通常是 Item。如果需要,也可以透過 IndexerNameAttribute 改變 metadata 中的名稱,但這不會讓索引器變成真正的「運算符重載替代品」。

物件初始化器
屬性很常和 object initializer 一起出現:
Employee e1 = new Employee() { Name = "Kelvin", Age = 24 };
Employee e2 = new Employee { Name = "Catherine", Age = 24 };
它看起來像是語法層面一次完成「建構 + 設值」,但實際流程仍然是:
- 先呼叫建構函式建立物件
- 再逐一呼叫屬性 setter
概念上就是:
Employee e = new Employee();
e.Name = "Kelvin";
e.Age = 24;
如果型別同時實作了 IEnumerable 且提供 Add(...),集合初始化器也會以類似方式被展開成一連串 Add(...) 呼叫。
Dictionary<string, int> table = new Dictionary<string, int>
{
{ "Kelvin", 24 },
{ "Catherine", 24 },
{ "Aden", 1000 },
};
存取修飾與 accessor 可見性
屬性本身和 get / set 並不一定要擁有完全相同的可見性。常見情境是「外部可讀,但只能在內部寫」:
public string Name { get; private set; }
這種寫法很常用在:
- 建構後不希望外部任意修改
- 由型別內部控制狀態轉換
- 想對外公開資料,但不公開寫入權限
可以把它理解成:屬性是 API 介面,而 accessor 則是更細粒度的權限控制點。
屬性與欄位的差別
最值得記住的不是語法,而是用途邊界。
欄位更像儲存空間
- 沒有攔截邏輯
- 沒有 validation
- 不適合直接當穩定公開 API
屬性更像受控入口
- 可以加入驗證、延遲計算與快取
- 可以在不改呼叫語法的情況下調整內部行為
- 更適合作為公開契約的一部分
因此實務上通常會偏向:
- 私有狀態:欄位
- 對外暴露:屬性
效能
屬性本質上是方法呼叫,但簡單 accessor 常常會被 JIT inline,因此在大多數一般場景裡,不需要把「屬性比欄位慢」當成主要設計依據。
真正該在意的是:
- 這個 accessor 裡是否放了昂貴邏輯
- 屬性名稱是否讓人誤以為是 O(1) 存取,實際卻很重
如果某個操作成本高、會做 I/O、或會造成明顯副作用,那它通常就不應該偽裝成普通屬性。
小結
屬性這章最值得留下的幾個點:
- 屬性底層是 accessor 方法加上 metadata,不是特殊欄位。
- 自動實作屬性適合簡單資料承載,但不是萬能解法。
- 索引器本質上是帶參數的屬性。
- 物件初始化器最後仍會落回建構函式與 setter 呼叫。
- 欄位偏向內部儲存,屬性偏向公開且受控的資料入口。
參考資料
- 《CLR via C#》