在日常開發中,我們經常面對各種集合類型:List、Hashtable、ArrayList 等。它們內部存儲結構各異,若客戶代碼直接依賴具體集合的實現細節(如下標訪問、鍵值遍歷),一旦需要替換集合類型,代碼將面臨大規模修改,違反開閉原則。
迭代器模式的核心目的在於:提供一種統一的方式順序訪問集合元素,而無需暴露集合的內部表示。
我們可以定義兩個介面:ICollection 負責集合操作,Iterator 負責遍歷邏輯。每種具體集合(如 List)對應一個專屬迭代器(ListIterator)。
public interface ICollection {
int Count { get; }
Iterator GetIterator();
}
public interface Iterator {
bool MoveNext();
object Current { get; }
}
public class List : ICollection {
private object[] items = new object[10];
public Iterator GetIterator() => new ListIterator(this);
// ... 索引器、Count 實現
}
public class ListIterator : Iterator {
private int index = 0;
private ICollection list;
public ListIterator(ICollection list) { this.list = list; }
public bool MoveNext() { /* 邏輯 */ }
public object Current { get { /* 邏輯 */ } }
}
客戶代碼僅依賴介面:
ICollection list = new List();
Iterator iterator = list.GetIterator();
while (iterator.MoveNext()) {
object current = iterator.Current;
// 處理元素
}
此時若將 List 換為 Hashtable,只需提供對應的 HashtableIterator,客戶代碼完全不變。
微軟在 .NET 基礎類庫中將此模式標準化:
IEnumerable:對應 ICollection,唯一方法 GetEnumerator() 返回迭代器。IEnumerator:對應 Iterator,包含 MoveNext()、Current、Reset()。所有集合類(ArrayList、List<T>、Dictionary<TKey,TValue> 等)均實現 IEnumerable,使得統一遍歷成為可能:
IEnumerator enumerator = list.GetEnumerator();
while (enumerator.MoveNext()) {
object item = enumerator.Current;
}
手寫 while 迴圈繁瑣且易出錯,C# 提供 foreach 關鍵字。編譯器會將其展開為標準的 try/finally 模式:
GetEnumerator() 獲取迭代器。try 區塊中循環調用 MoveNext() 並讀取 Current。finally 區塊中檢查迭代器是否實現 IDisposable,若是則調用 Dispose() 釋放非托管資源。關鍵差異: 手寫 while 迴圈若拋出異常,迭代器可能不會被正確釋放;foreach 保證資源釋放,更安全穩健。
C# 2.0 引入 yield return / yield break,讓開發者無需手寫 IEnumerator 實現類,即可輕鬆創建自定義迭代器:
public IEnumerable<int> GetNumbers() {
yield return 1;
yield return 2;
yield return 3;
}
// 編譯器自動生成實現 IEnumerable/IEnumerator 的狀態機類
yield 的「奇怪」特性在於:方法執行會在每次 yield return 處暫停,下次 MoveNext() 時從原處繼續。這種惰性求值特性是 LINQ 延遲執行的基石。
從手寫迭代器模式,到 .NET 標準化 IEnumerable/IEnumerator,再到 foreach 語法糖與 yield 迭代器塊,C# 的迭代機制經歷了「模式 → 框架 → 語法 → 編譯器生成」的演進。理解這一脈絡,不僅能寫出更優雅的代碼,更能深入掌握 LINQ 等高級特性的底層原理。