一、什么是迭代器
迭代器(Iterator)又称光标(Cursor)
提供一个方法顺序访问一个聚合对象的各个元素而不暴露内部标识
迭代器可用作方法、运算符或 get 访问器的代码体。
迭代器提供明确的语法,用于指定如何迭代集合类中的数据,尤其是使用 foreach 循环。
这样一来,集合的最终用户就可以浏览其内部结构,而无需知道相应结构。
迭代器方法的返回值类型可以是以下4种接口类型中任意一种:
- 位于命名空间System.Collections中的IEnumerable、IEnumerator
- 位于命名空间System.Collections.Generic中的IEnumerable<T>、IEnumerator<T>
迭代器方法的内部使用状态机实现,但可以使用yield关键字快速实现迭代器方法,使用yield return语句添加需要迭代的元素,
在首次迭代时,会一直执行到第一个yield return语句并保存当前迭代状态,在接下来的每次迭代过程中都会从暂停的位置继续执行到下一个yield return语句并保存迭代状态(MoveNext()方法返回true并将当前迭代的值赋值给Current),
直到到达迭代器的结尾(MoveNext()方法返回false)完成本次迭代;
也可以在迭代过程中使用yield break语句立刻结束本次迭代
C# 2.0 新增了 yield 上下文关键字,这样类就可以更轻松地决定 foreach 循环如何迭代其内容。
二、使用迭代器
使用迭代器创建枚举器
namespace ConsoleApp2 { public class MyClass { /// <summary> /// 该类目前已实现GetEnumerator()使类本身可枚举 /// </summary> /// <returns></returns> public IEnumerator<string> GetEnumerator()//迭代器 { yield return "black"; yield return "gray"; yield return "white"; } } internal class Program { static void Main(string[] args) { var mc = new MyClass(); foreach (string shade in mc)////该类目前已实现GetEnumerator()使类本身可枚举 所以 写mc { Console.WriteLine(shade); } Console.ReadKey(); } } }
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 + " "); } } }
三、疑问记录
IEnumerator和IEnumerable 的区别是什么?
直观上的区别是在使用上面,c#规定foreach只能遍历IEnumerable,当我们foreach一个IEnumerator时,编辑器会提示错误:
我们去遍历一个IEnumerator时,需要去循环地调用它的MoveNext()方法,然后.Current获取当前值。
yield关键字如何使用?
对于yield关键字,要看里面最关键的一个函数:MoveNext。
MoveNext其实很简单,switch(1,2,3,4)的意思是顺序跳转,第一次进入MoveNext走分支1,第二次进入走分支2......以此类推。
而我们的yield关键字被分成了3步,刷新current值为yield的return值,刷新state值,如果还有下一个yield则返回true否则返回false。
迭代器的使用场景?
我们想要遍历一个对象的时候,就可以去实现一个IEnumrable或者IEnumrator。
yield关键字的使用场景,对于unity开发者,最棒的例子就是unity的协程啦。
unity的协程用迭代器来实现异步操作,比如yield return new WaitForSeconds(),等待多少s继续往下走。