迭代器概述
迭代器是可以返回相同類型的值的有序序列的一段代碼。
迭代器可用作方法、運算子或 get 訪問器的代碼體。
迭代器代碼使用 yield return 語句依次返回每個元素。yield break 將終止迭代。有關更多資訊,請參見 yield。
可以在類中實現多個迭代器。每個迭代器都必須像任何類成員一樣有唯一的名稱,並且可以在 foreach 語句中被用戶端代碼調用,如下所示:foreach(int x in SampleClass.Iterator2){}
迭代器的傳回型別必須為 IEnumerable、IEnumerator、IEnumerable<T> 或 IEnumerator<T>。
迭代器是使用在foreach中的集合。在C#2.0中使用迭代器建立一個用於foreach的集合,實現上比較簡單:繼承於IEumnerable,並實現GetEnumerator()。
首先這個集合要基於IEnumerable(可以使用泛型),下面先來實現一個非泛型版的迭代器。代碼如下(非泛型程式碼範例來源於MSDN):
public class DaysOfTheWeek : System.Collections.IEnumerable { string[] m_Days = { "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat" }; public System.Collections.IEnumerator GetEnumerator() { for (int i = 0; i < m_Days.Length; i++) { yield return m_Days[i]; } } } class TestDaysOfTheWeek { static void Main() { DaysOfTheWeek week = new DaysOfTheWeek(); foreach (string day in week) { System.Console.Write(day + " "); } Console.Read(); } }
操作結果是:
Sun Mon Tue Wed Thr Fri Sat
其中yied return關鍵字產生枚舉元素
泛型版迭代器的實現代碼如下:
static void Main(string[] args) { Stack<int> stack = new Stack<int>(); stack.items = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; foreach (int i in stack) { Console.WriteLine(i); } Console.Read(); }public class Stack<T> : IEnumerable<T> { public T[] items; public IEnumerator<T> GetEnumerator() { for (int i = 0; i < items.Length; i++) { yield return items[i]; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }}
運行結果如下:
1
2
3
4
5
6
7
8
9
10
其中G在實現泛型迭代器時我一直沒有寫IEnumerator IEnumerable.GetEnumerator()這個方法,所以編譯器一直在給我報錯,(猜想)這個方法應該IEnumerator<T>介面中的一個抽象方法。而這個方法中調用的GetEnumerator(),通過Integration Environment中的提示發現,實際上是上面寫到的IEnumerator<T> GetEnumerator()這個泛型方法。
在自訂迭代器時,我們可以利用yield break關鍵字跳出迴圈。如上面的例子中,我們想只輸出小於等於5的項,調整上面代碼,如:
public class Stack<T> : IEnumerable<T>
{
public T[] items;
public IEnumerator<T> GetEnumerator()
{
for (int i = 0; i < items.Length; i++)
{
if ((Convert.ToInt32(items[i]) > 5))
yield break;
yield return items[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
操作結果:
1
2
3
4
5
迭代器的機制:
實際上迭代器只是在C#2.0中通過編譯器一層額外處理的,用來簡化建立可用於foreach的枚舉集合的工作,從效能上沒有什麼變化。對於其產生的中繼語言沒有太多的變化。
執行個體:定義和使用命名迭代器
class Class1
{
public IEnumerator GetEnumerator()
{
for (int i = 0; i < 10; i++)
{
yield return i;
}
}
// 定義一個命名的迭代器,並可以提供參數
public IEnumerable MaxToMin(int min, int max)
{
for (int i = max; i >= min; i--)
{
yield return i;
}
}
// 定義一個迭代器類型的屬性,
public IEnumerable MinToMax
{
// this表示該類執行個體,因為該類實現了GetEnumerator(),它是可枚舉的
get { yield return this; }
}
public IEnumerable GetDescriptions()
{
yield return "this is my test";
yield return "class name is class1";
yield return "ktgu";
}
}
static void Main(string[] args)
{
Class1 c = new Class1();
foreach (int i in c)
{
Console.WriteLine(i);
}
foreach (int i in c.MaxToMin(1, 10))
{
Console.WriteLine(i);
}
foreach (int i in c.MinToMax)
{
Console.WriteLine(i);
}
foreach (string s in c.GetDescriptions())
{
Console.WriteLine(s);
}
}
迭代效果及實現要點
1.迭代抽象:訪問一個彙總對象的內容而無需暴露它的內部表示。
2.迭代多態:為遍曆不同的集合結構提供一個統一的介面,從而支援同樣的演算法在不同的集合結構上進行操作。
3.迭代器的健壯性考慮:遍曆的同時更改迭代器所在的集合結構,會導致問題。
適用性
1.訪問一個彙總對象的內容而無需暴露它的內部表示。
2.支援對彙總對象的多種遍曆。
3.為遍曆不同的彙總結構提供一個統一的介面(即, 支援多態迭代)。
總結
Iterator模式就是分離了集合對象的遍曆行為,抽象出一個迭代器類來負責,這樣既可以做到不暴露集合的內部結構,又可讓外部代碼透明的訪問集合內部的資料
參考:
C#2.0-迭代器應用 http://cs.alienwave.cn/Topic/1347.aspx
迭代器(C# 編程指南) http://msdn2.microsoft.com/zh-cn/library/dscyy5s0(VS.80).aspx