代碼對比
// 完整寫法:帶邏輯控制
private string _loginname;
public string UserName {
get { return _loginname; }
set {
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException("用戶名不可為空");
_loginname = value.Trim();
}
}
// 自動屬性:純語法糖
public string UserName { get; set; }
// 等價於編譯器自動生成隱私欄位 + 簡單讀寫
核心區別
| 維度 | 完整寫法 | 自動屬性 |
| 邏輯控制 | 可驗證、轉換、觸發事件、延遲載入 | 無,直接賦值/返回 |
| 封裝性 | 完全控制存取可見性(如 private set) | C# 6+ 支援 get; private set; |
| 序列化 | 需標記 [DataMember] 於屬性 | 同樣適用 |
| 除錯體驗 | 可在 get/set 設斷點 | VS 可直接在屬性行命中 |
| 效能 | 極微小呼叫開銷(內聯優化後可忽略) | 相同 |
| 二進制相容性 | 修改邏輯不改簽名 | 同樣 |
何時選擇哪種?
✅ 必須用完整寫法的場景
- 輸入驗證(非空、長度、格式、業務規則)
- 衍生計算(如
FullName => FirstName + " " + LastName)
- 屬性變更通知(
INotifyPropertyChanged)
- 延遲初始化(首次存取才查詢資料庫/快取)
- 舊版相容:需在 set 中同步更新其他欄位
✅ 推薦用自動屬性的場景
- 單純資料傳輸物件 (DTO/POCO/ViewModel)
- Entity Framework / Dapper 實體類
- 無任何副作用的純資料容器
- 初期原型開發,後續可平滑重構為完整寫法
現代 C# 進階語法(兼具簡潔與控制)
// C# 7+ 運算式主體成員
private string _name;
public string Name {
get => _name;
set => _name = value?.Trim() ?? throw new ArgumentNullException(nameof(value));
}
// C# 9+ init-only 屬性(物件初始化後不可變)
public string UserName { get; init; }
// C# 10+ record 類型(自動不可變 + with 表達式)
public record UserDto(string UserName, string Email);
最佳實踐建議
- 預設使用自動屬性,保持代碼乾淨。
- 當業務邏輯需求出現時,重構為完整寫法(VS `Encapsulate Field` 重構功能一鍵完成)。
- 實體類採用
private set + 建構函數注入,確保領域模型有效性。
- 避免在 get/set 中執行耗時 I/O 或拋出非預期異常,違反最小驚訝原則。