C#学习笔记---迭代器
foreach隐式调用无参的GetEnumerator 方法来获取一个enumerator,但是一个集合只能有一个无参的GetEnumerator 方法,通常有这样的需求,根据参数的不同实现不同的枚举顺序或者枚举方式.在这些情形下,可以利用iterator来实现返回enumerable interfaces的属性和方法.比如Stack<T>实现了IEnumerable<T>类型的TopToBottom 和BottomToTop
using System.Collections.Generic;
public class Stack<T>: IEnumerable<T>
{
T[] items;
int count;
public void Push(T data) {}
public T Pop() {}
public IEnumerator<T> GetEnumerator() {
for (int i = count – 1; i >= 0; --i) {
yield return items[i];
}
}
public IEnumerable<T> TopToBottom {
get {
return this;
}
}
public IEnumerable<T> BottomToTop {
get {
for (int i = 0; i < count; i++) {
yield return items[i];
}
}
}
}
TopToBottom 属性简单的返回this,因为statck本身就是enumerable,BottomToTop 用iterator返回了一个enumerable ,下面的例子展示如何使用这些属性来枚举栈中的成员.public class Stack<T>: IEnumerable<T>
{
T[] items;
int count;
public void Push(T data) {}
public T Pop() {}
public IEnumerator<T> GetEnumerator() {
for (int i = count – 1; i >= 0; --i) {
yield return items[i];
}
}
public IEnumerable<T> TopToBottom {
get {
return this;
}
}
public IEnumerable<T> BottomToTop {
get {
for (int i = 0; i < count; i++) {
yield return items[i];
}
}
}
}
using System;
class Test
{
static void Main() {
Stack<int> stack = new Stack<int>();
for (int i = 0; i < 10; i++) stack.Push(i);
foreach (int i in stack.TopToBottom) Console.Write("{0} ", i);
Console.WriteLine();
foreach (int i in stack.BottomToTop) Console.Write("{0} ", i);
Console.WriteLine();
}
}
当然这些属性也可以用在出foreach语句之外的地方,下面的例子展示了通过单独的Print 方法调用属性的结果.这个例子也展示了用iterator实现的含参的FromToBy 方法.class Test
{
static void Main() {
Stack<int> stack = new Stack<int>();
for (int i = 0; i < 10; i++) stack.Push(i);
foreach (int i in stack.TopToBottom) Console.Write("{0} ", i);
Console.WriteLine();
foreach (int i in stack.BottomToTop) Console.Write("{0} ", i);
Console.WriteLine();
}
}
using System;
using System.Collections.Generic;
class Test
{
static void Print(IEnumerable<int> collection) {
foreach (int i in collection) Console.Write("{0} ", i);
Console.WriteLine();
}
static IEnumerable<int> FromToBy(int from, int to, int by) {
for (int i = from; i <= to; i += by) {
yield return i;
}
}
static void Main() {
Stack<int> stack = new Stack<int>();
for (int i = 0; i < 10; i++) stack.Push(i);
Print(stack.TopToBottom);
Print(stack.BottomToTop);
Print(FromToBy(10, 20, 2));
}
}
范型或者非范型的enumerable interfaces 接口都包含唯一的成员无参和返回类型为enumerable interface的GetEnumerator 方法,一个enumerable 做为一个enumerator 工厂(factory),欠当地实现enumerables 保证每次调用GetEnumerator 方法时产生独立的enumerators,如果enumerable 内部的状态在两次调用GetEnumerator没有改变,两次返回的enumerators 应该是顺序相同的同一个数据集.这个必须得到保证即使enumerators的声明周期交迭.using System.Collections.Generic;
class Test
{
static void Print(IEnumerable<int> collection) {
foreach (int i in collection) Console.Write("{0} ", i);
Console.WriteLine();
}
static IEnumerable<int> FromToBy(int from, int to, int by) {
for (int i = from; i <= to; i += by) {
yield return i;
}
}
static void Main() {
Stack<int> stack = new Stack<int>();
for (int i = 0; i < 10; i++) stack.Push(i);
Print(stack.TopToBottom);
Print(stack.BottomToTop);
Print(FromToBy(10, 20, 2));
}
}
using System;
using System.Collections.Generic;
class Test
{
static IEnumerable<int> FromTo(int from, int to) {
while (from <= to) yield return from++;
}
static void Main() {
IEnumerable<int> e = FromTo(1, 10);
foreach (int x in e) {
foreach (int y in e) {
Console.Write("{0,3} ", x * y);
}
Console.WriteLine();
}
}
}
上面的代码实现了简单的从1到10的乘法表,注意方法FromTo只被调用了一次来获取enumerable e,但是e.GetEnumerator()方法调用了多次(通过foreach表达式)来产生多个相同的enumerators,这些enumerators 都封装了FromTo方法中指定的iterator代码,注意迭代器代码修改From的值.但是这些enumerators表现为各自独立,因为每个enumerator有自己的From和To参数的拷贝.enumerators 之间的短暂状态的共享是在实现enumerables 和enumerators的时候应该避免的比较容易违反的隐含缺陷,C# iterator被设计用来避免这些问题,从而用简单和直观的方式实现健壮的enumerables 和enumerators.
using System.Collections.Generic;
class Test
{
static IEnumerable<int> FromTo(int from, int to) {
while (from <= to) yield return from++;
}
static void Main() {
IEnumerable<int> e = FromTo(1, 10);
foreach (int x in e) {
foreach (int y in e) {
Console.Write("{0,3} ", x * y);
}
Console.WriteLine();
}
}
}