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);
}

运行结果

命名迭代器是很有用的结构,因为一个自定义容器可以定义多重方式来请求返回的集。

posted @ 2012-08-05 12:28  tc庄稼汉  阅读(147)  评论(0编辑  收藏  举报