| 方式 | 封裝性 | 安全性 | 程式碼簡潔度 | 效能 |
|---|---|---|---|---|
| 改為 public 欄位 | ❌ 破壞 | ❌ 直接暴露 | ✅ 最簡單 | 最快 (直接記憶體存取) |
| Getter/Setter 方法 | ✅ 保持 | ✅ 可驗證 | ❌ 冗長 | 方法呼叫開銷 |
| **屬性** | ✅ 保持 | ✅ 可驗證 | ✅ **語法糖簡潔** | **JIT 內聯 → 接近欄位速度** |
private string _name;
public string Name
{
get => _name;
set => _name = value ?? throw new ArgumentNullException(nameof(value));
}
// 編譯器產生對應方法 (IL)
.get instance string Class::get_Name()
.set instance void Class::set_Name(string)
// JIT 會將這兩個方法內聯,消除呼叫開銷
public class DataTable
{
private List _rows = new();
public Row this[int index]
{
get => _rows[index];
set => _rows[index] = value;
}
// 多參數
public Cell this[int row, string colName]
{
get => _rows[row].Cells[colName];
set => _rows[row].Cells[colName] = value;
}
}
private string _config;
private readonly object _lock = new();
public string Config
{
get { lock (_lock) return _config; }
set { lock (_lock) _config = value; }
}
// 或 .NET 4.7+ 使用互斥體更輕量
// 讀多寫少考慮 ReaderWriterLockSlim
同一類別實作 IReadOnly{ string Name { get; } } 和 IWritable{ string Name { get; set; } } 會造成介面轉型時行為不同,**不建議使用**。
public class Person
{
private Address _address;
public Address Address
{
get => _address?.Clone(); // 回傳副本,防止外部修改內部狀態
set => _address = value?.Clone();
}
}
public class Address : ICloneable
{
public string City { get; set; }
public object Clone() => MemberwiseClone(); // 淺層複製足夠 (字串不可變)
}
**原則**:屬性回傳可變參考型別時,**務必回傳副本** (Clone/ToArray/ToList/new List(...)),否則呼叫端可繞過 setter 直接修改內部狀態。
public int Age
{
get => _age;
set
{
if (value < 0 || value > 150) throw new ArgumentOutOfRangeException(nameof(value));
_age = value;
}
}
屬性是**外部修改內部狀態的唯一入口**,驗證邏輯集中於此,保證物件不變條件。
// 傳統自動屬性 (編譯器產生隱藏 backing field)
public string Name { get; set; }
// 唯讀自動屬性 (建構函式/物件初始化器可指定)
public string Id { get; init; }
// 唯讀 + 預設值
public DateTime CreatedAt { get; init; } = DateTime.UtcNow;
GetComputedValue()。readonly struct 的自動屬性 getter 會被 JIT 積極內聯與優化。**屬性是 C# 封裝的核心機制** — 兼具欄位語法簡潔性、方法驗證彈性、JIT 內聯效能。遵循「回傳參考型別需複製、Setter 必驗證、複雜計算用方法」三原則,即能設計出健壯、可維護的類別 API。