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

Effective C# 原則 7:選擇不可變的原子實值型別(完)

     編輯:關於C語言

防禦性複製:保護不可變型別內的參考型別欄位

public readonly struct PhoneList
{
    private readonly Phone[] _phones;

    public PhoneList(Phone[] phones)
    {
        _phones = new Phone[phones.Length];
        phones.CopyTo(_phones, 0); // 防禦性複製:Phone 為 struct 故複製值
    }

    public IEnumerator GetEnumerator() => ((IEnumerable)_phones).GetEnumerator();
}

// 使用
var phones = new Phone[10];
// 初始化 phones...
var list = new PhoneList(phones);
phones[5] = Phone.Generate(); // 不影響 list 內部陣列

屬性存取也需防禦性複製

public Phone[] Phones
{
    get
    {
        var copy = new Phone[_phones.Length];
        _phones.CopyTo(copy, 0);
        return copy;
    }
}

建立不可變物件的三種模式

模式說明範例
建構函式單次呼叫完整初始化new Address("1F", "Taipei", "100")
工廠方法靜態方法封裝建立邏輯Color.FromArgb(r,g,b), Color.FromKnownColor
建構器/伴隨類別多步驟建構,最後產出不可變物件StringBuilder → string

Address 不可變結構完整實作

public readonly struct Address
{
    public string Line1 { get; }
    public string Line2 { get; }
    public string City { get; }
    public string State { get; }
    public int ZipCode { get; }

    public Address(string line1, string line2, string city, string state, int zipCode)
    {
        Line1 = line1 ?? throw new ArgumentNullException(nameof(line1));
        Line2 = line2 ?? "";
        City = city ?? throw new ArgumentNullException(nameof(city));
        State = state ?? throw new ArgumentNullException(nameof(state));
        ZipCode = zipCode;
        ValidateState(state);
        ValidateZip(zipCode);
    }

    // 修改城市 → 回傳新實例
    public Address WithCity(string newCity) => new Address(Line1, Line2, newCity, State, ZipCode);
}

使用方式

var a1 = new Address("111 S. Main", "", "Anytown", "IL", 61111);
// 修改城市
var a2 = a1.WithCity("Ann Arbor"); // a1 不變,a2 為新實例
// 或使用 with 運算式 (C# 9+)
var a3 = a1 with { City = "Ann Arbor", State = "MI", ZipCode = 48103 };

不可變型別的核心優勢

  • 執行緒安全:無狀態變更,天然免鎖
  • 可做雜湊鍵:GetHashCode() 穩定,適合 Dictionary、HashSet
  • 可安全暴露:呼叫端無法竄改內部狀態
  • 推理簡單:物件建立後狀態固定,減少認知負荷
  • 支援 with 運算式:C# 9+ 語法糖輕鬆產生修改版

總結

預設設計不可變實值型別。將複雜物件分解為原子不可變單元(Address、Phone、Name),再組合成較大結構。避免盲目加上 set 存取器,優先考慮 WithXxx() 或 with 運算式回傳新實例。

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