C#笔记13:迭代器
本章概要:
1:迭代器概述
2:创建迭代器
3:创建多个迭代器
4:泛型中的迭代器
1:迭代器概述
迭代器是可以返回相同类型的值的有序序列的一段代码。
迭代器可用作方法、运算符或 get 访问器的代码体。
迭代器代码使用 yield return 语句依次返回每个元素。yield break 将终止迭代。
可以在类中实现多个迭代器。每个迭代器都必须像任何类成员一样有唯一的名称,并且可以在 foreach 语句中被客户端代码调用,如下所示:foreach(int x in SampleClass.Iterator2){}。
迭代器的返回类型必须为 IEnumerable、IEnumerator、IEnumerable<(Of <(T>)>) 或 IEnumerator<(Of <(T>)>)。
迭代器是 LINQ 查询中延迟执行行为的基础。
yield 关键字用于指定返回的一个或多个值。到达 yield return 语句时,会保存当前位置。下次调用迭代器时将从此位置重新开始执行。迭代器对集合类特别有用,它提供一种简单的方法来迭代复杂的数据结构(如二进制树)。
2:创建迭代器
创建迭代器最常用的方法是对 IEnumerable 接口实现 GetEnumerator 方法,查看如下代码:
public class DaysOfTheWeek : System.Collections.IEnumerable { string[] days = { "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat" }; public System.Collections.IEnumerator GetEnumerator() { for (int i = 0; i < days.Length; i++) { yield return days[i]; } } } class TestDaysOfTheWeek { static void Main() { // Create an instance of the collection class DaysOfTheWeek week = new DaysOfTheWeek(); // Iterate with foreach foreach (string day in week) { System.Console.Write(day + " "); } } } // Output: Sun Mon Tue Wed Thr Fri Sat
foreach 语句在背后调用 DaysOfTheWeek.GetEnumerator() 并使用返回的枚举数来循环访问值。
3:创建多个迭代器
还可以使用命名的迭代器以支持通过不同的方式循环访问同一数据集合。例如,您可以提供一个按升序返回元素的迭代器,而提供按降序返回元素的另一个迭代器。迭代器还可以带有参数,以便允许客户端控制全部或部分迭代行为。
// Implementing the enumerable pattern public System.Collections.IEnumerable SampleIterator(int start, int end) { for (int i = start; i <= end; i++) { yield return i; } }
命名的迭代器的调用方法如下:
foreach (int n in test.SampleIterator(1, 10)) { System.Console.Write(n + " "); } // Output: 1 2 3 4 5 6 7 8 9 10
4:泛型中的迭代器
在此示例中,泛型类 Stack<T> 实现泛型接口 IEnumerator<(Of <(T>)>)。 声明了一个类型 T 的数组,并使用 Push 方法给数组赋值。 在 GetEnumerator 方法中,使用 yield return 语句返回数组的值。
还实现非泛型 GetEnumerator,因为 IEnumerable<(Of <(T>)>) 继承自 IEnumerable。 此示例显示了典型的实现,其中,非泛型方法仅将调用转给泛型方法。
using System.Collections; using System.Collections.Generic; namespace GenericIteratorExample { public class Stack<T> : IEnumerable<T> { private T[] values = new T[100]; private int top = 0; public void Push(T t) { values[top++] = t; } public T Pop() { return values[--top]; } // These make Stack<T> implement IEnumerable<T> allowing // a stack to be used in a foreach statement. public IEnumerator<T> GetEnumerator() { for (int i = top - 1; i >= 0; i-- ) { yield return values[i]; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } // Iterate from top to bottom. public IEnumerable<T> TopToBottom { get { // Since we implement IEnumerable<T> // and the default iteration is top to bottom, // just return the object. return this; } } // Iterate from bottom to top. public IEnumerable<T> BottomToTop { get { for (int i = 0; i < top; i++) { yield return values[i]; } } } // A parameterized iterator that return n items from the top public IEnumerable<T> TopN(int n) { // in this example we return less than N if necessary int j = n >= top ? 0 : top - n; for (int i = top; --i >= j; ) { yield return values[i]; } } } // This code uses a stack and the TopToBottom and BottomToTop properties // to enumerate the elements of the stack. class Test { static void Main() { Stack<int> s = new Stack<int>(); for (int i = 0; i < 10; i++) { s.Push(i); } // Prints: 9 8 7 6 5 4 3 2 1 0 // Foreach legal since s implements IEnumerable<int> foreach (int n in s) { System.Console.Write("{0} ", n); } System.Console.WriteLine(); // Prints: 9 8 7 6 5 4 3 2 1 0 // Foreach legal since s.TopToBottom returns IEnumerable<int> foreach (int n in s.TopToBottom) { System.Console.Write("{0} ", n); } System.Console.WriteLine(); // Prints: 0 1 2 3 4 5 6 7 8 9 // Foreach legal since s.BottomToTop returns IEnumerable<int> foreach (int n in s.BottomToTop) { System.Console.Write("{0} ", n); } System.Console.WriteLine(); // Prints: 9 8 7 6 5 4 3 // Foreach legal since s.TopN returns IEnumerable<int> foreach (int n in s.TopN(7)) { System.Console.Write("{0} ", n); } System.Console.WriteLine(); // Keep the console window open in debug mode. System.Console.WriteLine("Press any key to exit."); System.Console.ReadKey(); } } } /* Output: 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 0 1 2 3 4 5 6 7 8 9 9 8 7 6 5 4 3 */