一、传统遍历
传统遍历即通过集合类实现IEnumerable、IEnumerator 或IEnumerable
1,分析:
1)从这两个接口上看:
a) IEnumerable 声明式接口,声明实现该接口的 类是可以枚举的。
b) IEnumerator 实现式接口,IEnumerator对象说明如何实现枚举器。
2)Foreach语句隐式调用集合的无参GetEnumerator方法(不论集合是否实现IEnumerable接口,但只要有无参GetEnumerator方法并返回IEnumerator就可以遍历)
3)集合类为什么不直接实现IEnumerable和IEnumerator接口?
这样可降低并发性。Eg:一个遍历机制只有一个Current,一旦并发就会出错。然而"将遍历机制与集合分离开来" 如果要实现同一个遍历,只需集合IEnumerator.GetEnumerator() 返回一个新的包含遍历机制(IEnumerator)的类实例即可。
2,调用过程
3,具体实现示例
二、迭代器(iterator)
迭代器是C#2.0中的新功能。它使类或结构支持foreach迭代,而不必"显示"实现IEnumerable或IEnumerator接口。只要简单的石油yield关键字,由JIT编译器编译成实现IEnumerable 或IEnumerator接口的对象(即 本质还是传统遍历,只是写法上非常简洁)。
1,分析
1)yield语句只能出现在iterator块(迭代块)中,该块只能用作方法、运算符或get访问器的主题实现。该类方法、运算符或访问器的“主体”受以下约束的控制:
a)不允许不安全块。
b)方法、运算符或访问器的参数不能是ref或out。
2)迭代器代码使用yield return 语句依次返回每个元素。yield break 将终止迭代。
a)yield return 的时候会保存当前位置(状态机)并把控制权从迭代器交给调用的程序,做必要的返回值处理,下一次进入迭代器将重之前保存的位置处开始执行直到迭代结束或调用yield break.
b)yield break 就是控制权交给调用程序就不回来了从而终止迭代。
3) yield return 语句不能放在 try-catch 块中。但可放在后跟 finally 块的 try 块中。
4) yield break 语句可放在 try 块或 catch 块中,但不能放在 finally 块中。
5) yield 语句不能出现在匿名方法中。
6) 迭代器必须返回相同类型的值,因为最后输出为IEnumerator.Current是单一类型。(见下面示例)
7) 在同一个迭代器中可以使用多个 yield 语句。(见下面示例)
8) 自定义迭代器:迭代器可以自定义名称、可以带参数,但在foreach中需要显示去调用自定义的迭代器。(见下面示例)
9) 迭代器的返回类型必须为IEnumerator、IEnumerator
1) 返回类型为IEnumerable、IEnumerator
返回此类型的迭代器方法必须满足:
a)必须有GetEnumerator且不带参数;
b)必须是Public 公共成员;
将CourseCollection集合对象的IEnumerable.GetEnumerator()方法实现如下:
经过 JIT 编译后,会自动生成一个实现了 IEnumerator以上可知:
a)同一个迭代器中有多少个yield return 语句,while 循环中就有多少个return true。
b)yield return 结束本次循环,yield break 结束整个循环。输出数据的顺序通过生成类中的一个state状态字段做为 switch 标识来决定要输出第几个 yield return。yield return
在每个case里面改变state内部字段,使正确执行完多个return返回数据,并最后通过return true来结束本次MoveNext()。而yield break语句直接生成break并重置state状态字段为switch中没有的值而跳出switch语句,通过执行最后的return false来结束整个循环。
c)注意:yield return 后面的 List
2)返回类型为IEnumerable、IEnumerable
返回此类型的迭代器必须满足:
a)必须可以在foreach语句中被调用(访问权限);
返回此类型的迭代器通常用于实现自定义迭代器,即:迭代器可以自定义名称、可以带参数。Eg:(升序和降序)
需如下进行迭代器调用:
经过 JIT 编译后,会自动生成一个直接实现IEnumerator
这是因为在不同foreach遍历中所访问的由编译器自动生成的迭代器具有其自己独立的状态,所以迭代器之间互不影响,不存在并发的问题。