小菜个人水平有限,所以不足之处,请大家不要吝啬拍砖.
微软的.Net Framework源码到:http://www.cnblogs.com/ghost_boy/archive/2008/07/16/1244229.html 下载.
Mono.Net Framework源码到:http://go-mono.com/sources-stable/ 下载第一个,现在是mono-1.9.1.tar.bz2.
两个源码质量都很高,反正就是比小菜高.

System.Collections开始吧,集合对于我们来说太熟悉不过了,我们天天都在用它.
Array,ArrayList,Stack,Queue等等

而且现在小菜热血沸腾,要不等下灭了就不好了.

)foreach还未来到这个世界,遍历集合的世界是怎么样的?    (挺黑暗的,信啊,那你接着往下瞧)
.Net Framework框架模式(第一篇 迭代器模式)public class Person
我们先定义一个Person,这个对我们来说太简单不过,一个人有姓名有年龄. 
.Net Framework框架模式(第一篇 迭代器模式)static void Main(string[] args)

看看小菜做了些什么,,小菜使用Array来存储Person,并且遍历该Array
一些朋友开始不满了,小菜你就让我们看这个啊,这也太简单了吧.( :) 那让我们接着往下看,会有更多惊喜)

.Net Framework框架模式(第一篇 迭代器模式)static void Main(string[] args)
看看小菜又做了些什么,,小菜改用ArrayList来存储Person,并且遍历ArrayList.

我们先来看看ArrayListArray遍历的不同之处吧!
1)Array使用persons.Length来确定长度,ArrayList使用persons.Count来确定长.
2)Array使用persons.GetValue(i)来取出当前元素,ArrayList使用persons[i]取出当前元素.
一些朋友开始抱怨代码还是太简单了,那就来个有难度点的Stack()
不知道大家还记不记得这个数组结构中的老大级人物(后进先出)

.Net Framework框架模式(第一篇 迭代器模式)static void Main(string[] args)

不知道有没有朋友产生疑问?
问题1) for(int i=0; i<persons.Count;)中怎么没有i++?
问题2) for(int i=0; i<persons.Count;){/*省略*/} 省略中的代码替换成如下代码又会出现什么问题呢?
Console.WriteLine("姓名:" + ((Person)persons.Pop()).Name);
Console.WriteLine("姓名:" + ((Person)persons.Pop()).Name);
输出结果:    姓名:小北 年龄:13    姓名:小西 年龄:11
?奇了怪了怎么小南与小东没有被输出呢

至于为什么,我们来走一遍运行过程吧.
1. i=0; persons.Count=4; p= 小北
2. i=0; persons.Count=3; p= 小南
3. i=0; persons.Count=2; p= 小西
4. i=0; persons.Count=1; p= 小东
Stack中的Pop会返回栈顶元素,并弹出,所以会将count减一,并且数组的索引减一.

所以使用上面的代码,Pop()两次,相当于count减二,数组的索引减二,所以小南和小东不会被输出.
我们通过Stack中的关键源码来加深下理解.

.Net Framework框架模式(第一篇 迭代器模式)public class Stack

看来我们遇到麻烦了,如果我们不理解Stack这个数据结构,那么对它的遍历操作我们将无法实现.
们的设计一直提倡针对接口编程,而现在我们却在针对实现编程,看来挺失败的.
具体的数据结构将在很大程度上影响我们的实现.

如果你明白了这一句话那么小菜就没有必要在举例: 队列Queue,哈希表HashTable等等了,小菜也轻松了. :)

)foreach将我们脱离了苦海.(那么是谁让foreach脱离了苦海呢?)
Iterator模式让foreach脱离苦海.
接下来就让我们进入迭代模式(Iterator模式),这个都快被大家遗忘的模式.
我们先来看看foreach的实现吧!

.Net Framework框架模式(第一篇 迭代器模式)static void Main(string[] args)

确实简单,我们无需知道遍历集合的任何比较细节.
我们无需知道遍历集合的任何实现.
foreach是怎么做到的呢?
们来看看foreach被翻译成什么(代码已简化)

.Net Framework框架模式(第一篇 迭代器模式)IEnumerator enumerator = persons.GetEnumerator();
.Net Framework框架模式(第一篇 迭代器模式)
//persons可以是上面的Array,ArrayList,Stack的任何一种
.Net Framework框架模式(第一篇 迭代器模式)
try
!上面的代码是怎么回事?
时我们将用到一个最重要的设计原则: 对象的职责划分
对象集合的职责是存储对象,遍历对象集合的行为是不属于集合的职责.
所以我们可以将遍历对象集合的行为独立出来成为接口:IEnumerator

对象集合遍历的操作不外乎:1.判断是否还有下一个元素 MoveNext() 2.取出当前元素 Current
那么我们就来定义一个接口吧:IEnumerator(称为枚举器,名称不同罢了,功能是相同的,java中的接口Iterator迭代器一样)
.Net Framework框架模式(第一篇 迭代器模式)public interface IEnumerator
对象集合将构造自己的IEnumerator,这样才有foreach的遍历功能.
Array开始吧.
.Net Framework框架模式(第一篇 迭代器模式)public abstract class Array : IEnumerable

 
这里说下:return new SZArrayEnumerator(this)表示遍历的数组为一维的时候使用它.
return new ArrayEnumerator(this,lowerBound,Length);表示遍历的数组为多维的时候使用它.
lowerBound: 为第一维的下限
Lenght: 为所有维数中元素的总数
遍历多维的实现也很有趣,主要使用两个函数一个属性:
1.array.GetUpperBound(i) i维的上限 2.array.GetLowerBound(i) i维的下限 3.array.Rank 有几维
如果你对具体实现感兴趣,那就打开你手中的.Net Framework中的Array.cs源码吧.

按这样说
,ArrayList也应该有类似的实现.那就让我们来看看ArrayList的源码,来验证这个想法.

.Net Framework框架模式(第一篇 迭代器模式)public class ArrayList


看来事实验证了我们的想法.
还有什么可以改进的呢?
我们看到ArrayArrayList等各对象集合都实现了 IEnumerator GetEnumerator()
那么我们是不是应该为它们定义一个接口呢?当然. IEnumerable

.Net Framework框架模式(第一篇 迭代器模式)public interface IEnumerable
这也就是为什么我们的Array,ArrayList,Stack等都实现了IEnumerable接口.
现在就让我们来看看UML,让我们更清晰点.
.Net Framework框架模式(第一篇 迭代器模式)
这便是迭代模式.
它在.Net Framework中真可谓无处不在.
.Net Framework框架模式(第一篇 迭代器模式)string name = "a-peng";
.Net Framework框架模式(第一篇 迭代器模式)
foreach(char c in name)
现在大家看到这个代码会联想到什么呢?
(应该会联想到迭代模式吧,foreach便是它给予的魔力,如果还联想不到,那小菜真是太失败了)
看来string也应该有提供类似的代码.否则它怎么能foreach?
那就让我们来看看String的代码吧.(stringString的别名而已)
.Net Framework框架模式(第一篇 迭代器模式)public sealed class String : IEnumerable
CharEnumerator支持循环访问String对象,并读取它的各个字符.
类似之前的Array中的SZArrayEnumerator  还有 ArrayList中的ArrayListEnumeratorSimple
.Net Framework框架模式(第一篇 迭代器模式)public sealed class CharEnumerator : IEnumerator
)自己来实现一个支持遍历的集合.
.Net2.0的时候引入了泛型,所以也对IEnumerableIEnumerator做了改进.
引入了IEnumerable<T>IEnumerator<T>
.Net Framework框架模式(第一篇 迭代器模式)using System;
.Net Framework框架模式(第一篇 迭代器模式)
namespace System.Collections.Generic

.Net Framework框架模式(第一篇 迭代器模式)using System;
.Net Framework框架模式(第一篇 迭代器模式)
namespace System.Collections.Generic
很清晰不是吗,在原来的基础上扩展了下.
那看来小菜也要与时俱进.来实现个.
.Net Framework框架模式(第一篇 迭代器模式)using System;
.Net Framework框架模式(第一篇 迭代器模式)
using System.Collections.Generic;
.Net Framework框架模式(第一篇 迭代器模式)
.Net Framework框架模式(第一篇 迭代器模式)
namespace APeng

其实上面的public class Persons : IEnumerable<Person>,System.Collections.IEnumerable
完全可以用public class Persons : IEnumerable<Person>来替换,不过为了增强代码的可读性,所以小菜加上.
这种做法很类似微软的ArrayList : IList , ICollection, IEnumerable 也可被替换为 ArrayList : IList

需要注意的地方
1.实现IEnumerable<T> 要记得实现IEnumerable
2.同理实现IEnumerator<T> 要记得实现IEnumeratorIDisposable
那接下来就来使用Persons

.Net Framework框架模式(第一篇 迭代器模式)static void Main(string[] args)

其实上面的两种遍历方法是等效的,可相互转换.
)有没有更简单的实现方式啊.有使用yield (.Net2.0中引入的关键字)

.Net Framework框架模式(第一篇 迭代器模式)using System;
.Net Framework框架模式(第一篇 迭代器模式)
using System.Collections.Generic;
.Net Framework框架模式(第一篇 迭代器模式)
.Net Framework框架模式(第一篇 迭代器模式)
namespace APeng
GetEnumerator函数是一个C# 2.0支持的迭代块(iterator block).
通过
yield诉编译器在什么时候返回什么值,再由编译器自动完成实现IEnumerator<Person>接口的登记工作。
我们还可以使用
yield break语句支持从迭代块中直接结束.

:) 就到这里.

相关文章: