C#中的迭代器
迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象内部的表示。迭代器为遍历不同的聚集结构提供如开始、下一个、是否结束、当前哪一项等统一的借口。
迭代器模式结构图:
Iterator类
1 /////////////////////////////////////////////////////////// 2 // Iterator.cs 3 // Implementation of the Class Iterator 4 // Generated by Enterprise Architect 5 // Created on: 23-七月-2011 13:44:42 6 // Original author: xinyuanzhang 7 /////////////////////////////////////////////////////////// 8 9 10 11 namespace Test 12 { 13 /// <summary> 14 /// 迭代抽象类 15 /// </summary> 16 public abstract class Iterator 17 { 18 19 public Iterator() 20 { 21 22 } 23 24 ~Iterator() 25 { 26 27 } 28 29 public virtual void Dispose() 30 { 31 32 } 33 34 public abstract object CurrentItem(); 35 36 public abstract object First(); 37 38 public abstract bool IsDone(); 39 40 public abstract object Next(); 41 42 }//end Iterator 43 }
Aggregate聚集抽象类
1 /////////////////////////////////////////////////////////// 2 // Aggregate.cs 3 // Implementation of the Class Aggregate 4 // Generated by Enterprise Architect 5 // Created on: 23-七月-2011 13:46:01 6 // Original author: xinyuanzhang 7 /////////////////////////////////////////////////////////// 8 9 10 11 namespace Test 12 { 13 /// <summary> 14 /// 聚集抽象类 15 /// </summary> 16 public abstract class Aggregate 17 { 18 19 public Aggregate() 20 { 21 22 } 23 24 ~Aggregate() 25 { 26 27 } 28 29 public virtual void Dispose() 30 { 31 32 } 33 34 public abstract Iterator CreateIterator(); 35 36 }//end Aggregate 37 }
ConcreteIterato具体迭代器类
1 /////////////////////////////////////////////////////////// 2 // ConcreteIterator.cs 3 // Implementation of the Class ConcreteIterator 4 // Generated by Enterprise Architect 5 // Created on: 23-七月-2011 13:48:10 6 // Original author: xinyuanzhang 7 /////////////////////////////////////////////////////////// 8 9 10 11 namespace Test 12 { 13 public class ConcreteIterator : Iterator 14 { 15 16 //定义有一个具体聚集对象 17 public ConcreteAggregate m_ConcreteAggregate; 18 private int current = 0; 19 20 public ConcreteIterator() 21 { 22 } 23 24 //初始化聚集对象传入 25 public ConcreteIterator(ConcreteAggregate aggregate) 26 { 27 this.m_ConcreteAggregate = aggregate; 28 } 29 30 ~ConcreteIterator() 31 { 32 33 } 34 35 public override void Dispose() 36 { 37 38 } 39 40 /// <summary> 41 /// 返回当前聚集对象 42 /// </summary> 43 /// <returns></returns> 44 public override object CurrentItem() 45 { 46 47 return m_ConcreteAggregate[current]; 48 } 49 50 /// <summary> 51 /// 得到聚集的第一个对象 52 /// </summary> 53 /// <returns></returns> 54 public override object First() 55 { 56 57 return m_ConcreteAggregate[0]; 58 } 59 60 /// <summary> 61 /// 判断当前是否遍历到结尾 62 /// </summary> 63 /// <returns></returns> 64 public override bool IsDone() 65 { 66 67 return current >= m_ConcreteAggregate.Count ? true : false; 68 } 69 70 /// <summary> 71 /// 得到聚集的下一个对象 72 /// </summary> 73 /// <returns></returns> 74 public override object Next() 75 { 76 77 object ret = null; 78 current++; 79 if (current < m_ConcreteAggregate.Count) 80 { 81 ret = m_ConcreteAggregate[current]; 82 } 83 return ret; 84 } 85 86 }//end ConcreteIterator 87 }
ConcreteAggregate具体聚集类
1 /////////////////////////////////////////////////////////// 2 // ConcreteAggregate.cs 3 // Implementation of the Class ConcreteAggregate 4 // Generated by Enterprise Architect 5 // Created on: 23-七月-2011 13:49:53 6 // Original author: xinyuanzhang 7 /////////////////////////////////////////////////////////// 8 9 using System.Collections.Generic; 10 11 namespace Test 12 { 13 public class ConcreteAggregate : Aggregate 14 { 15 16 public ConcreteAggregate() 17 { 18 19 } 20 21 ~ConcreteAggregate() 22 { 23 24 } 25 26 public override void Dispose() 27 { 28 29 } 30 31 public override Iterator CreateIterator() 32 { 33 34 return new ConcreteIterator(this); 35 } 36 37 //声明一个IList<T>变量,用于存放聚合对象 38 private IList<object> items = new List<object>(); 39 40 //返回集合总个数 41 public int Count 42 { 43 get { return items.Count; } 44 } 45 46 //声明一个索引器 47 public object this[int index] 48 { 49 get { return items[index]; } 50 set 51 { 52 items.Insert(index, value); 53 } 54 } 55 56 57 }//end ConcreteAggregate 58 }
客户端代码
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Data.Common; 6 using System.Data; 7 8 namespace Test 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 ConcreteAggregate a = new ConcreteAggregate(); 15 16 a[0] = "A"; 17 a[1] = "B"; 18 a[2] = "C"; 19 a[3] = "D"; 20 a[4] = "E"; 21 a[5] = "F"; 22 a[6] = "G"; 23 24 Iterator i = new ConcreteIterator(a); 25 object item = i.First(); 26 while (!i.IsDone()) 27 { 28 Console.WriteLine(i.CurrentItem()); 29 i.Next(); 30 } 31 32 Console.ReadKey(); 33 } 34 } 35 }
运行结果
在实际使用中.NET框架已经准备好了相关接口,C#支持关键字foreach,允许我们遍历任何数组类型的内容。任何支持GetEnumerator()方法的类型都可以通过foreach结构进行运算。这个方法是由IEnumerable接口定义的。对象支持这种行为说明它们能够向调用方法提供自己包含的子项。
1 //这个接口告知调用方法的子项可以枚举 2 public interface IEnumerable 3 { 4 IEnumerator GetIEnumerator(); 5 }
GetEnumerator()方法返回一个IEumerator的引用。这个接口提供了基础设施,调用方法可以用来移动IEnumerable兼容容器包含的内部对象:
1 //这个接口允许调用方法获取一个容器的子项 2 public interface IEnumerator 3 { 4 bool MoveNext();//将光标的内部位置向前移动。 5 object Currect{get;}//获取当前的项 6 void Reset();//将光标重置到第一个成员前面 7 }
可以修改自定义类型使之支持这些接口,一个更简单的方法是Array和其它许多类型已经实现了这2个接口,可以简单的请求到Array,如下所示:
1 using System; 2 using System.Collections; 3 using System.Linq; 4 using System.Text; 5 6 namespace Test 7 { 8 public class LetterSet:IEnumerable 9 { 10 private Letter[] letterArr = new Letter[7]; 11 12 public LetterSet() 13 { 14 letterArr[0] = new Letter(1,"A"); 15 letterArr[1] = new Letter(2,"B"); 16 letterArr[2] = new Letter(3,"C"); 17 letterArr[3] = new Letter(4,"D"); 18 letterArr[4] = new Letter(5,"E"); 19 letterArr[5] = new Letter(6,"F"); 20 letterArr[6] = new Letter(7,"G"); 21 } 22 23 public IEnumerator GetEnumerator() 24 { 25 return letterArr.GetEnumerator(); 26 } 27 } 28 }
客户端代码
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Data.Common; 6 using System.Data; 7 8 namespace Test 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 LetterSet ls = new LetterSet(); 15 foreach (Letter l in ls) 16 { 17 Console.WriteLine(l.Name); 18 } 19 20 Console.ReadKey(); 21 } 22 } 23 }
上面还不是最简单的方法,从.Net2.0以后,可以通过迭代器来构建使用foreach的类型。
简单来说,迭代器指定了容器内部项被foreach处理时该如何返回,虽然迭代器方法还是必须命名为GetEnumerator(),返回值还是IEumerator类型,但自定义类不需要实现原来那些接口了:
1 using System; 2 using System.Collections; 3 using System.Linq; 4 using System.Text; 5 6 namespace Test 7 { 8 public class LetterSet 9 { 10 private Letter[] letterArr = new Letter[7]; 11 12 public LetterSet() 13 { 14 letterArr[0] = new Letter(1,"A"); 15 letterArr[1] = new Letter(2,"B"); 16 letterArr[2] = new Letter(3,"C"); 17 letterArr[3] = new Letter(4,"D"); 18 letterArr[4] = new Letter(5,"E"); 19 letterArr[5] = new Letter(6,"F"); 20 letterArr[6] = new Letter(7,"G"); 21 } 22 23 public IEnumerator GetEnumerator() 24 { 25 foreach (Letter l in letterArr) 26 { 27 yield return l; 28 } 29 } 30 } 31 }
例中上GetEnumerator()的实现使用内部foreach逻辑迭代每个项,使用yield返回语法后,当前位置被存储下来,下次调用迭代器会从这个位置开始执行。也可以不使用foreach关键字,下面的用法并不推荐,因为当实例增多时,GetEnumerator()就不会同步,但是该方法的运行结果可以帮助我们理解yield关键字。
1 public IEnumerator GetEnumerator() 2 { 3 yield return letterArr[0]; 4 yield return letterArr[1]; 5 yield return letterArr[2]; 6 yield return letterArr[3]; 7 yield return letterArr[6]; 8 yield return letterArr[5]; 9 yield return letterArr[4]; 10 }
运行结果如下:
yield关键字可以和任何方法一起使用,这些方法也被称为“命名迭代器”,其独特之处在于可以接受参数。构建命名迭代器时,这些方法必须定义为返回IEnumerable类型,而不是IEumerator类型,下例显示了一个通过参数决定排列顺序的方法:
1 public IEnumerable GetLetters(bool IsASC) 2 { 3 if (IsASC) 4 { 5 foreach (Letter l in letterArr) 6 { 7 yield return l; 8 } 9 } 10 else 11 { 12 for (int i = letterArr.Length; i != 0; i--) 13 { 14 yield return letterArr[i - 1]; 15 } 16 } 17 }
客户端代码
foreach (Letter l in ls.GetLetters(false))
{
Console.WriteLine(l.Name);
}
运行结果
命名迭代器是很有用的结构,因为一个自定义容器可以定义多重方式来请求返回的集。