系列目录 【已更新最新开发文章,点击查看详细】 <https://www.cnblogs.com/SavionZhang/p/11229640.html>
迭代器可用于逐步迭代集合,例如列表和数组。
迭代器方法或 get 访问器可对集合执行自定义迭代。 迭代器方法使用 yield return
<https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/yield>
语句返回元素,每次返回一个。到达 yield return 语句时,会记住当前在代码中的位置。 下次调用迭代器函数时,将从该位置重新开始执行。
通过 foreach
<https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/foreach-in>
语句或 LINQ 查询从客户端代码中使用迭代器。
在以下示例中,foreach 循环的首次迭代导致 SomeNumbers 迭代器方法继续执行,直至到达第一个 yield return 语句。
此迭代返回的值为 3,并保留当前在迭代器方法中的位置。在循环的下次迭代中,迭代器方法的执行将从其暂停的位置继续,直至到达 yield return
语句后才会停止。此迭代返回的值为 5,并再次保留当前在迭代器方法中的位置。 到达迭代器方法的结尾时,循环便已完成。
static void Main() { foreach (int number in SomeNumbers()) {
Console.Write(number.ToString()+ " "); } // 输出: 3 5 8 Console.ReadKey(); }
public static System.Collections.IEnumerable SomeNumbers() { yield return 3;
yield return 5; yield return 8; }
迭代器方法或 get 访问器的返回类型可以是 IEnumerable
<https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.ienumerable>、
IEnumerable<T>
<https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.generic.ienumerable-1>
、IEnumerator
<https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.ienumerator> 或
IEnumerator<T>
<https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.generic.ienumerator-1>
。
可以使用 yield break 语句来终止迭代。
对于本主题中除简单迭代器示例以外的所有示例,请为 System.Collections 和 System.Collections.Generic
命名空间加入 using 指令。
简单的迭代器 下例包含一个位于 for
<https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/for>
循环内的yield return 语句。 在 Main 中,foreach 语句体的每次迭代都会创建一个对迭代器函数的调用,并将继续到下一个 yield
return 语句。 static void Main() { foreach (int number in EvenSequence(5, 18)) {
Console.Write(number.ToString()+ " "); } // 输出: 6 8 10 12 14 16 18
Console.ReadKey(); }public static System.Collections.Generic.IEnumerable<int>
EvenSequence(int firstNumber, int lastNumber) { // 迭代集合中的偶数. for (int number =
firstNumber; number <= lastNumber; number++) { if (number % 2 == 0) { yield
return number; } } } 创建集合类
在以下示例中,DaysOfTheWeek 类实现 IEnumerable
<https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.ienumerable>
接口,此操作需要GetEnumerator
<https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.ienumerable.getenumerator>
方法。编译器隐式调用 GetEnumerator 方法,此方法返回 IEnumerator
<https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.ienumerator>。
GetEnumerator 方法通过使用 yield return 语句每次返回 1 个字符串。
static void Main() { DaysOfTheWeek days = new DaysOfTheWeek(); foreach (string
dayin days) { Console.Write(day + " "); } // 输出: Sun Mon Tue Wed Thu Fri Sat
Console.ReadKey(); }public class DaysOfTheWeek : IEnumerable { private string[]
days = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; public IEnumerator
GetEnumerator() {for (int index = 0; index < days.Length; index++) { // 迭代每一天
yield return days[index]; } } }
下例创建了一个包含动物集合的 Zoo 类。
引用类实例 (theZoo) 的 foreach 语句隐式调用 GetEnumerator 方法。 引用 Birds 和 Mammals 属性的
foreach 语句使用 AnimalsForType 命名迭代器方法。
1 static void Main() 2 { 3 Zoo theZoo = new Zoo(); 4 5 theZoo.AddMammal("
Whale"); 6 theZoo.AddMammal("Rhinoceros"); 7 theZoo.AddBird("Penguin"); 8
theZoo.AddBird("Warbler"); 9 10 foreach (string name in theZoo) 11 { 12
Console.Write(name +" "); 13 } 14 Console.WriteLine(); 15 // 输出: Whale
Rhinoceros Penguin Warbler 16 17 foreach (string name in theZoo.Birds) 18 { 19
Console.Write(name +" "); 20 } 21 Console.WriteLine(); 22 // 输出: Penguin
Warbler 23 24 foreach (string name in theZoo.Mammals) 25 { 26
Console.Write(name +" "); 27 } 28 Console.WriteLine(); 29 // 输出: Whale
Rhinoceros 30 31 Console.ReadKey(); 32 } 33 34 public class Zoo : IEnumerable
35 { 36 // 私有成员 37 private List<Animal> animals = new List<Animal>(); 38 39 //
公共方法 40 public void AddMammal(string name) 41 { 42 animals.Add(new Animal {
Name = name, Type = Animal.TypeEnum.Mammal }); 43 } 44 45 public void AddBird(
string name) 46 { 47 animals.Add(new Animal { Name = name, Type =
Animal.TypeEnum.Bird });48 } 49 50 public IEnumerator GetEnumerator() 51 { 52
foreach (Animal theAnimal in animals) 53 { 54 yield return theAnimal.Name; 55
}56 } 57 58 // 公共成员 59 public IEnumerable Mammals 60 { 61 get { return
AnimalsForType(Animal.TypeEnum.Mammal); }62 } 63 64 public IEnumerable Birds 65
{66 get { return AnimalsForType(Animal.TypeEnum.Bird); } 67 } 68 69 // 私有方法 70
private IEnumerable AnimalsForType(Animal.TypeEnum type) 71 { 72 foreach
(Animal theAnimalin animals) 73 { 74 if (theAnimal.Type == type) 75 { 76 yield
return theAnimal.Name; 77 } 78 } 79 } 80 81 // 私有类 82 private class Animal 83
{84 public enum TypeEnum { Bird, Mammal } 85 86 public string Name { get; set;
}87 public TypeEnum Type { get; set; } 88 } 89 } 对泛型列表使用迭代器
在以下示例中,Stack<T>
<https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.generic.stack-1>
泛型类实现IEnumerable<T>
<https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.generic.ienumerable-1>
泛型接口。Push
<https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.generic.stack-1.push>
方法将值分配给类型为T 的数组。 GetEnumerator
<https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.generic.ienumerable-1.getenumerator>
方法通过使用yield return 语句返回数组值。
除了泛型 GetEnumerator
<https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.generic.ienumerable-1.getenumerator>
方法,还必须实现非泛型GetEnumerator
<https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.ienumerable.getenumerator>
方法。这是因为从 IEnumerable
<https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.ienumerable> 继承了
IEnumerable<T>
<https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.generic.ienumerable-1>
。非泛型实现遵从泛型实现的规则。
本示例使用命名迭代器来支持通过各种方法循环访问同一数据集合。 这些命名迭代器为 TopToBottom 和 BottomToTop 属性,以及 TopN
方法。
BottomToTop 属性在 get 访问器中使用迭代器。
1 static void Main() 2 { 3 Stack<int> theStack = new Stack<int>(); 4 5 //
向堆栈中添加项 6 for (int number = 0; number <= 9; number++) 7 { 8
theStack.Push(number); 9 } 10 11 // 从堆栈中检索项。 12 // 此处允许使用 foreach,因为
foreach 实现了 IEnumerable<int> 13 foreach (int number in theStack) 14 { 15
Console.Write("{0} ", number); 16 } 17 Console.WriteLine(); 18 // 输出: 9 8
7 6 5 4 3 2 1 0 19 20 // 此处允许使用 foreach,因为 theStack.TopToBottom 属性返回了
IEnumerable(Of Integer). 21 foreach (int number in theStack.TopToBottom) 22 {
23 Console.Write("{0} ", number); 24 } 25 Console.WriteLine(); 26 // 输出:
9 8 7 6 5 4 3 2 1 0 27 28 foreach (int number in theStack.BottomToTop) 29 {
30 Console.Write("{0} ", number); 31 } 32 Console.WriteLine(); 33 // 输出:
0 1 2 3 4 5 6 7 8 9 34 35 foreach (int number in theStack.TopN(7)) 36 { 37
Console.Write("{0} ", number); 38 } 39 Console.WriteLine(); 40 // 输出: 9 8
7 6 5 4 3 41 42 Console.ReadKey(); 43 } 44 45 public class Stack<T> :
IEnumerable<T> 46 { 47 private T[] values = new T[100]; 48 private int top = 0
; 49 50 public void Push(T t) 51 { 52 values[top] = t; 53 top++; 54 } 55
public T Pop() 56 { 57 top--; 58 return values[top]; 59 } 60 61 //
此方法实现了GetEnumerator()方法. 它允许在 foreach 语句中使用类的实例。 63 public IEnumerator<T>
GetEnumerator() 64 { 65 for (int index = top - 1; index >= 0; index--) 66 {
67 yield return values[index]; 68 } 69 } 70 71 IEnumerator
IEnumerable.GetEnumerator() 72 { 73 return GetEnumerator(); 74 } 75 76
public IEnumerable<T> TopToBottom 77 { 78 get { return this; } 79 } 80 81
public IEnumerable<T> BottomToTop 82 { 83 get 84 { 85 for (int index = 0;
index <= top -1; index++) 86 { 87 yield return values[index]; 88 } 89 }
90 } 91 92 public IEnumerable<T> TopN(int itemsFromTop) 93 { 94 //
如有必要,返回少于 itemsFromTop 95 int startIndex = itemsFromTop >= top ? 0 : top -
itemsFromTop; 96 97 for (int index = top - 1; index >= startIndex; index--) 98
{ 99 yield return values[index]; 100 } 101 } 102 103 } 语法信息
迭代器可用作一种方法,或一个 get 访问器。 不能在事件、实例构造函数、静态构造函数或静态终结器中使用迭代器。
必须存在从 yield return 语句中的表达式类型到迭代器返回的 IEnumerable<T> 类型参数的隐式转换。
在 C# 中,迭代器方法不能有任何 in、ref 或 out 参数。
在 C# 中,“yield”不是保留字,只有在 return 或 break 关键字之前使用时才有特殊含义。
技术实现
即使将迭代器编写成方法,编译器也会将其转换为实际上是状态机的嵌套类。 只要客户端代码中的 foreach 循环继续,此类就会跟踪迭代器的位置。
若要查看编译器执行的操作,可使用 Ildasm.exe 工具查看为迭代器方法生成的 Microsoft 中间语言代码。
为类
<https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/class>
或结构
<https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/struct>
创建迭代器时,不必实现整个IEnumerator
<https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.ienumerator> 接口。
编译器检测到迭代器时,会自动生成IEnumerator
<https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.ienumerator> 或
IEnumerator<T>
<https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.generic.ienumerator-1>
接口的Current、MoveNext 和 Dispose 方法。
在 foreach 循环(或对 IEnumerator.MoveNext 的直接调用)的每次后续迭代中,下一个迭代器代码体都会在上一个 yield
return 语句之后恢复。 然后继续下一个 yield return 语句,直至到达迭代器体的结尾,或直至遇到 yield break 语句。
迭代器不支持 IEnumerator.Reset
<https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.ienumerator.reset>
方法。若要从头开始重新迭代,必须获取新的迭代器。 在迭代器方法返回的迭代器上调用 Reset
<https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.ienumerator.reset>
会引发NotSupportedException
<https://docs.microsoft.com/zh-cn/dotnet/api/system.notsupportedexception>。
有关其他信息,请参阅 C# 语言规范
<https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/language-specification/classes#iterators>
。
迭代器的使用
需要使用复杂代码填充列表序列时,使用迭代器可保持 foreach 循环的简单性。 需执行以下操作时,这可能很有用:
*
在第一次 foreach 循环迭代之后,修改列表序列。
*
避免在 foreach 循环的第一次迭代之前完全加载大型列表。 一个示例是用于加载一批表格行的分页提取。 另一个示例关于 EnumerateFiles
<https://docs.microsoft.com/zh-cn/dotnet/api/system.io.directoryinfo.enumeratefiles>
方法,该方法在 .NET Framework 中实现迭代器。
*
在迭代器中封装生成列表。 使用迭代器方法,可生成该列表,然后在循环中产出每个结果。
其他内容请参阅
* System.Collections.Generic
<https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.generic>
* IEnumerable<T>
<https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.generic.ienumerable-1>
* foreach, in
<https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/foreach-in>
* yield
<https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/yield>
* 对数组使用 foreach
<https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/arrays/using-foreach-with-arrays>
* 泛型
<https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/generics/index>
系列目录 【已更新最新开发文章,点击查看详细】
<https://www.cnblogs.com/SavionZhang/p/11229640.html>
热门工具 换一换