首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

Effective C# 原則 7:優先選擇不可變的原子實值型別

     編輯:關於C#

不可變型別:建構後狀態凍結,絕不修改。優勢:

  • 建構時驗證一次,後續永無效狀態。
  • 天然線程安全:多線程同步讀取無不一致視圖。
  • 安全暴露內部資料:呼叫端無法竄改。
  • 完美支援雜湊容器:GetHashCode() 恆定(原則 10)。

原子型別:單一實體,不可分割

Address 是原子型別:街道、城市、州、郵遞區碼強相關,修改其一通常意味著整體變更。Customer 非原子:姓名、地址、電話可獨立變更,應拆解為多個不可變原子型別組合。

可變 Address 缺陷示範

struct Address { public string City { get; set; } public string State { get; set; } ... }
var a = new Address { City = "Taipei", State = "TP" };
a.City = "Taichung"; // 此刻 State 仍為 TP → 無效狀態!
a.State = "TXG";     // 修正前,任何線程讀取皆見不一致

多線程下極危險;單線程若拋出例外(郵遞區碼驗證失敗),物件殘留無效狀態。需大量防禦性程式碼修補。

改為不可變 Address

struct Address
{
    private readonly string _line1, _line2, _city, _state;
    private readonly int _zip;
    public string City => _city;
    // 僅 getter,無 setter
    public Address(string line1, string line2, string city, string state, int zip)
    {
        _line1 = line1; _line2 = line2; _city = city; _state = state; _zip = zip;
        ValidateState(state); ValidateZip(zip); // 建構時一次驗證
    }
}
// 使用:建立新實例取代修改
var a = new Address("111 Main", "", "Taipei", "TP", 100);
a = new Address(a._line1, a._line2, "Taichung", "TXG", 400); // 原子性更新

狀態永遠要麼是舊值、要麼是新值,無中間無效狀態。建構失敗拋例外時,原變數保持舊值不變。

不可變型別包含參考型別欄位:防禦性複製

// 危險:外部陣列修改影響內部狀態
struct PhoneList { private readonly Phone[] _phones; ... }

// 安全:建構時複製
struct PhoneList
{
    private readonly Phone[] _phones;
    public PhoneList(Phone[] phones)
    {
        _phones = new Phone[phones.Length];
        phones.CopyTo(_phones, 0); // Phone 為實值型別,複製值
    }
    // 回傳陣列時同樣複製
    public Phone[] Phones => (Phone[])_phones.Clone();
}

若欄位為可變參考型別,建構與回傳時皆需深層複製。

三種不可變物件建構模式

  1. 參數化建構函式:一次性傳入所有資料(Address)。
  2. 工廠方法:靜態方法封裝建構邏輯(Color.FromKnownColor())。
  3. 建構器模式:多步驟累積資料,最後產出不可變物件(StringBuilder → string)。

總結

勿盲目為所有屬性加 get/set。優先設計為不可變原子實值型別,再組合成複雜結構。簡單、安全、高效。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved