1. 前言
我們先來聊聊直接用欄位會遇到什麼問題。如果你把欄位宣告成 public,那它就可以被程式裡任何地方直接改掉:
public class Dog
{
public string Name;
}
Dog dog = new Dog();
dog.Name = ""; // O_o ... 這就像「狗的名字 = 空字串」!
使用者可以把 Name 欄位設成完全沒意義的值,像是空字串、太長的名字,甚至 null。這就像你讓人組裝你的 IKEA 新衣櫃,結果你直接給他木工廠的所有權限。
提醒一下,封裝 就是物件自己掌控自己的資料。我們不會讓每個路人都能碰內部資料,而是給一個專門的「門」——屬性(Properties),大家只能透過這個門進出,這樣你就能加檢查、記錄、修改或其他反應。
2. 定義和語法
屬性 是 class 的一種特殊成員,看起來很像欄位,但其實底層是兩個特殊方法:getter(拿值)和 setter(設值)。用屬性你可以:
- 允許(或禁止)讀/寫資料;
- 存取時加驗證或邏輯;
- 把內部欄位藏起來,甚至把值存到別的地方。
屬性的宣告很像欄位,只是多了大括號,裡面有 get 跟 set 關鍵字。
[存取修飾詞] 型別 屬性名稱
{
get { ... }
set { ... }
}
來看我們 Dog class 的例子:
public class Dog
{
private string _name; // 內部欄位(private!)
public string Name
{
get { return _name; } // "getter":拿名字
set { _name = value; } // "setter":設名字
}
}
小提醒: 底線通常用在 private 欄位(_name)。這是 C# 的寫法慣例。
3. 屬性的運作機制
屬性就像「守門員」,站在物件內部資料和外部世界之間。舉例:
Dog dog = new Dog();
dog.Name = "Шарик";
Console.WriteLine(dog.Name);
說明:
- 當程式跑到 dog.Name = "Шарик"; 這行時,其實是呼叫屬性的 set 方法,你可以在這裡加任何檢查(像是名字不能是空的)。
- 當 Console.WriteLine(dog.Name); 執行時,就是呼叫 get 方法,直接回傳目前的值,或是你想要動態算出來的東西。
看起來像一般欄位,其實一切都在你掌控中!
4. 為什麼屬性是 "Best Practice"
大多數時候我們不會讓外部直接碰物件的內部欄位。就算現在覺得不用檢查,養成用屬性包資料的習慣,等規則變了你會很感謝自己。
「設值時驗證」範例:
public class Dog
{
private string _name;
public string Name
{
get { return _name; }
set
{
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentException("狗的名字不能是空的!");
}
_name = value;
}
}
}
現在這樣:
dog.Name = ""; // 會丟出例外!
… 保護你的物件不會被設成奇怪的值。
5. 屬性:唯讀、唯寫、一般
有時你只想讓人看值(像狗的出生年,不能改),或只讓人設值(很少見,但你想搞神祕也可以)。
- 唯讀: 只寫 get,不寫 set。
- 唯寫: 只寫 set,不寫 get。
範例:
public class Dog
{
private int _birthYear = 2018;
// 唯讀
public int BirthYear
{
get { return _birthYear; }
}
// 唯寫(超少見)
public string Secret
{
set { /* 處理 value */ }
}
}
6. 屬性 vs 欄位
| 欄位 | 屬性 | |
|---|---|---|
| 語法 | |
|
| 存取 | 直接 | 透過 get/set |
| 驗證 | 沒有 | 可以加在 set/get |
| 擴充性 | 沒有 | 隨時可以改 |
| IDE 整合 | 看起來像欄位 | 看起來像屬性(對 framework 很重要) |
圖解:屬性的運作
sequenceDiagram
participant User as 物件的使用者
participant Dog as Dog 物件
participant Field as 私有欄位 _name
User->>Dog: dog.Name = "Рыжик"
Dog->>Dog: set Name("Рыжик")
Dog->>Field: _name = "Рыжик"
User->>Dog: print(dog.Name)
Dog->>Dog: get Name()
Dog->>Field: 讀取 _name
Dog->>User: 回傳 "Рыжик"
7. 常見錯誤和小細節
來看看會踩到哪些坑。
常見錯誤——搞混欄位和屬性,不小心把 private 資料暴露出去:
public string name; // 這是欄位!到處都看得到,危險!
正確寫法:
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
靜態屬性 也有,就是給所有物件共用的值。但要小心用。
有趣: 如果屬性只有 get,又沒 constructor,那它根本不能改——這叫 immutable property,在現代設計很常見(我們之後講 record 和資料不可變時會細講)。
GO TO FULL VERSION