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

C# 集合類實作全攻略:從 CollectionBase 繼承到 ArrayList 封裝,掌握 .NET Collections 核心技術

     編輯:關於C語言

前言

在物件導向專案中,常需將業務實體 (如 Customer) 組織為集合 (Customers),並支援索引存取、列舉、強型別約束等功能。.NET System.Collections 命名空間提供多種基礎類別,本文對比兩種主流實作路徑:繼承 CollectionBase 與 封裝 ArrayList,並深入探討索引器、Item 屬性、屬性封裝、延遲初始化等關鍵技術。

一、領域模型基礎類別

public class Customer
{
    public string Name { get; set; }
    // public Contacts Contacts { get; set; } // 集合關聯稍後說明

    public Customer() { Console.WriteLine("Initialize instance without parameter"); }
    public Customer(string name) { Name = name; Console.WriteLine("Initialize instance with parameter"); }
}

二、方案 A:繼承 CollectionBase

2.1 核心實作

public class Customers : CollectionBase
{
    public void Add(Customer c) => List.Add(c);
    public void Remove(int index)
    {
        if (index < 0 || index >= Count) Console.WriteLine("Index not valid!");
        else List.RemoveAt(index);
    }

    // 索引器
    public Customer this[int index]
    {
        get => (Customer)List[index];
        // 刻意不提供 set,形成唯讀索引器
    }

    // Item 方法 (供 VB 風格呼叫)
    public Customer Item(int index) => (Customer)List[index];

    // Item 屬性 (回傳 this 以支援 customers.Item[i] 語法)
    [IndexerName("Item")] // 指定編譯後索引器名稱避免衝突
    public Customers Item => this;
}

2.2 關鍵技術點

  • CollectionBase 內建 List:已實作 Count、Clear、RemoveAt 等 IList 介面,僅需補充強型別 Add/Remove。
  • 索引器編譯為 Item 屬性:C# 編譯器預設將索引器編譯為 Item 屬性,若同時定義 Item 方法/屬性會衝突。解決:在索引器前加 [IndexerName("item")] 指定別名。
  • Item 兩種實作:
    • 方法 Item(int):效能佳,語法 custs.Item(i)。
    • 屬性 Item { get { return this; } }:支援 custs.Item[i],但多一次索引器呼叫,效能略低。

三、方案 B:封裝 ArrayList

3.1 為何不繼承 ArrayList?

  • 繼承會暴露 Add(object),無法阻止加入非 Contact 物件,型別安全全失。
  • 取得元素仍需強制轉型,介面不乾淨。

3.2 封裝實作

public class Contacts
{
    private ArrayList _list = new ArrayList();

    public void Add(Contact c) => _list.Add(c);
    public void Remove(int index) { /* 檢查索引 */ _list.RemoveAt(index); }
    public int Count => _list.Count;

    // 完整索引器 (含 set,允許整個元素替換)
    public Contact this[int index]
    {
        get => (Contact)_list[index];
        set => _list[index] = value;
    }
}

public class Contact
{
    private string _summary;
    public string Summary
    {
        get { Console.WriteLine("getter Access"); return _summary; }
        set { Console.WriteLine("setter Access"); _summary = value; }
    }
}

3.3 索引器 set 的真正意義

contacts[0] = new Contact();        // 呼叫 set,替換整個元素
contacts[0].Summary = "New value";  // 呼叫 get 取得物件,再設其屬性,不觸發索引器 set

只有「替換集合元素本身」才會觸發 set;修改元素內部屬性則不會。依業務決定是否提供 set。

四、屬性:比欄位更強大的封裝

特性欄位屬性
驗證邏輯無set 可驗證
延遲初始化建構式立即執行get 時首次初始化
副作用/通知無get/set 可執行邏輯
介面統一性直接存取get/set 統一名稱

IL 層面屬性編譯為 get_Prop/set_Prop 兩方法,語法糖極大提升可讀性。

五、集合類內聯最佳實踐

不佳:public Contacts Contacts = new Contacts(); (公開欄位,無控制)

推薦:

private Contacts _contacts;
public Contacts Contacts
{
    get { if (_contacts == null) _contacts = new Contacts(); return _contacts; }
    set => _contacts = value;
}

實現 customers[i].Contacts[x].Summary 連續存取,並支援延遲初始化。

六、實例化時機:Lazy Initialization 平衡點

  • 值型別 (struct) 宣告即配置,無延遲問題。
  • 參考型別 (class) 應在「首次使用前」初始化:過早佔用資源,過晚影響回應速度。
  • 屬性 get 中判斷 null 建立,是常見平衡策略。

七、測試驗證

var custs = new Customers();
custs.Add(new Customer("Peter"));
custs.Add(new Customer("Linnet"));
Console.WriteLine(custs[0].Name);      // 索引器
Console.WriteLine(custs.Item(1).Name); // 方法
Console.WriteLine(custs.Item[1].Name); // Item 屬性+索引器
custs.Remove(0);
Console.WriteLine(custs[0].Name);      // Linnet
custs.Clear();

結語

繼承 CollectionBase 適合快速建立強型別唯讀集合;封裝 ArrayList 適合需完整控制增刪改查、型別安全與元素替換語意的場景。善用索引器、屬性、IndexerName 屬性與延遲初始化,能打造既符合 .NET 慣例、又具備領域表達力的集合類別。

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