.NET 內建 List<T>、Dictionary<TKey,TValue>、HashSet<T> 等集合已涵蓋 95% 場景。但特定業務需求可能需要:
OrderBook.AddBid() vs List.Add())foreach 支援,讓呼叫端程式碼更自然| 介面 | 角色 | 關鍵成員 | 泛型版本 |
|---|---|---|---|
IEnumerable | 可列舉物件 (容器) | GetEnumerator() | IEnumerable<T> |
IEnumerator | 列舉器 (游標) | MoveNext(), Reset(), Current | IEnumerator<T> |
using System;
using System.Collections;
using System.Collections.Generic;
public class Books : IEnumerable // 非泛型
{
private readonly ArrayList _titles = new ArrayList();
public void Add(string title) => _titles.Add(title);
public void Remove(string title) => _titles.Remove(title);
public int Count => _titles.Count;
// 必須實作:回傳列舉器
public IEnumerator GetEnumerator() => new BooksEnumerator(this);
}
// 獨立列舉器類別
public class BooksEnumerator : IEnumerator
{
private readonly Books _books;
private int _position = -1; // 游標初始位置
public BooksEnumerator(Books books) => _books = books;
public bool MoveNext()
{
if (_position < _books.Count - 1)
{
_position++;
return true;
}
return false;
}
public void Reset() => _position = -1;
public object Current
{
get
{
if (_position == -1 || _position >= _books.Count)
throw new InvalidOperationException("列舉器位置無效");
return _books._titles[_position];
}
}
}
// 使用範例
Books books = new Books();
books.Add("C# in Depth");
books.Add("CLR via C#");
books.Add("Effective C#");
foreach (string title in books) // 編譯器自動呼叫 GetEnumerator/MoveNext/Current
{
Console.WriteLine(title);
}
public class Books : IEnumerable // 泛型介面
{
private readonly List _titles = new List();
public void Add(string title) => _titles.Add(title);
public void Remove(string title) => _titles.Remove(title);
public int Count => _titles.Count;
// 泛型 GetEnumerator
public IEnumerator GetEnumerator() => new BooksEnumerator(this);
// 非泛型介面顯式實作 (相容性)
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
public class BooksEnumerator : IEnumerator
{
private readonly Books _books;
private int _position = -1;
public BooksEnumerator(Books books) => _books = books;
public string Current => _books._titles[_position]; // 強型別 Current
object IEnumerator.Current => Current; // 非泛型介面顯式實作
public bool MoveNext()
{
if (_position < _books.Count - 1)
{
_position++;
return true;
}
return false;
}
public void Reset() => _position = -1;
public void Dispose() { } // 通常無非托管資源,保留空實作
}
public class Books : IEnumerable
{
private readonly List _titles = new List();
public void Add(string title) => _titles.Add(title);
public void Remove(string title) => _titles.Remove(title);
public int Count => _titles.Count;
// yield return 自動生成列舉器類別 (編譯器魔法)
public IEnumerator GetEnumerator()
{
foreach (var title in _titles)
{
yield return title; // 狀態機自動管理 _position
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
public IEnumerable GetTitlesStartingWith(char prefix)
{
foreach (var title in _titles)
{
if (title.StartsWith(prefix.ToString(), StringComparison.OrdinalIgnoreCase))
yield return title;
}
}
var books = new Books();
books.Add("A");
books.Add("B");
var query = books.GetTitlesStartingWith('A'); // 此時尚未執行任何迴圈!
Console.WriteLine("查詢已建立,未執行");
foreach (var title in query) // 這裡才真正開始迭代
{
Console.WriteLine(title);
}
若需索引存取、插入、刪除、複製等完整集合行為,建議繼承 Collection<T> 或實作 IList<T>:
using System.Collections.ObjectModel;
public class Books : Collection // 繼承 Collection 即獲得 IList, ICollection, IEnumerable
{
// 可覆寫受保護方法自訂行為
protected override void InsertItem(int index, string item)
{
if (string.IsNullOrWhiteSpace(item))
throw new ArgumentException("書名不可為空");
base.InsertItem(index, item);
}
protected override void SetItem(int index, string item)
{
if (string.IsNullOrWhiteSpace(item))
throw new ArgumentException("書名不可為空");
base.SetItem(index, item);
}
}
| 原則 | 說明 |
|---|---|
優先使用 yield return | 程式碼簡潔、編譯器生成最佳化狀態機、自動實作 IDisposable |
回傳 IEnumerable<T> 而非 List<T> | 保護封裝、支援延遲執行、允許回傳陣列/迭代器/資料庫查詢 |
| 避免在迭代器中修改集合 | 會導致 InvalidOperationException (集合已修改),改用 ToList() 先快照 |
大量資料考慮 IAsyncEnumerable<T> | .NET Core 3.0+ 支援非同步串流,避免阻塞執行緒 |
實作 IReadOnlyCollection<T> / IReadOnlyList<T> | 唯讀語意明確,防止呼叫端誤修改 |
foreach 的合約基礎。Collection<T> 可快速獲得完整集合功能並保有擴充點。IEnumerable<T>、IReadOnlyList<T>) 提升彈性與可測試性。掌握自訂集合實作,能讓領域模型更豐富、API 更具表達力、封裝更嚴謹。