迭代器是一种对象,可以视为是一个集合的书签或者游标,表示一个集合中的某个位置,用以遍历集合中的部分或全部元素,它提供了一个方法顺序访问一个集合中的各个元素,而不暴露其内部表示,而C#已经为我们提供了几个迭代器的接口
枚举接口
枚举接口IEnumerable和IEnumerator是迭代器模式(iterator pattern)在C#中的实现。它们实现在集合上进行简单迭代的效果
IEnumerable接口
集合需要实现IEnumerable接口,才能被迭代,这个接口实现GetEnumerator方法,这个方法会返回一个IEnumerator对象,这就是我们的迭代器对象
IEnumerator接口
迭代器实现IEnumerator接口,这个接口有三个方法
- MoveNext
用于判断受否达到了迭代的终点 - Current
获取当前迭代的值 - Reset
重置迭代器
通过自定义类来实现枚举接口的功能
集合类
1 | public class GameEngineEnumerable : IEnumerable |
迭代器类
1 | public class GameEngineEnumerator : IEnumerator |
接下来实现 IEnumerator接口的三个方法
1 | public bool MoveNext() |
1 | public object Current |
1 | public void Reset() |
接下来,我们在主函数中实现遍历
1 | public static void Main(string[] args) |
成功输出string数组里面的值
使用yield语句简化迭代
我们把包含 yield 语句的方法或属性称为迭代块
按照以上的方式实现迭代显然太过繁琐,C#为我们提供了yield语句简化了这个过程,我们可以使用yield语句提供下一个值(yield return)或者表示迭代结束(yield break)
现在,我们注释掉GameEngineEnumerator类,将GetEnumerator方法改为以下代码
1 | public IEnumerator GetEnumerator() |
一样能实现相同的效果,但代码得到了极大的简化
而当我们执行yield break时,迭代器立即停止,MoveNext立即返回false,Current停留在最后一次保留的值上
在yield return执行完毕以后,函数并不会销毁,而是“休克”,等待返回值的IEnumerator执行下一次MoveNext(),yield return语句指定了枚举器中下一个可枚举项,迭代器在每次调用MoveNext函数时,会顺着上一次的枚举项(yield return)按照我们自己写的逻辑执行到下一个枚举项去
foreach
在C#中,我们可以使用foreach语句遍历可枚举集合,例如数组,哈希表,字典等,例如
1 | foreach (var item in str) |
而事实上,foreach内部就是由迭代器实现的,运行时并不直接支持 foreach 语句,C# 编译器会转换代码(foreach也类似于一种语法糖)
以上为官方的foreach写法,下面我们手动实现一个民间foreach
1 | void ForeachFunc(string str) |
我们调用这个方法,会发现输出的结果是一样的,实际上,foreach的实现其实非常简单,就类似于上面的民间foreach,对集合调用GetEnumerator方法获取迭代器(string继承了IEnumerable接口),然后使用while循环,直到MoveNext方法返回false,而在循环的过程中,可以调用Current属性,获取迭代器当前所对应的值
Unity中的协程
Unity中的协程正是由迭代器实现的,具体内容,请转到[[协程原理的简单认识]]
About this Post
This post is written by Yuan, licensed under CC BY-NC 4.0.