2012-04-29 00:44:12|?次阅读|上传:wustguangh【已有?条评论】发表评论
迭代器是一种方法、get 访问器或运算符,它通过使用 yield 关键字对数组或集合类执行自定义迭代。 yield 返回语句会导致源序列中的元素在访问源序列中的下一个元素之前立即返回给调用方。 尽管您以方法的形式编写迭代器,但编译器会将其转换为一个实际上是状态机的嵌套类。 只要客户端代码中的 foreach 循环继续进行,此类就会跟踪迭代器的位置。本文将对foreach的运行机制,传统集合的遍历以及使用迭代器进行遍历进行介绍。
我们在程序中经常会用到foreach,如果你把它理解成是for的一种简写形式的话那就太大材小用了,事实上foreach中包含了丰富的内 容。我们知道要使用foreach遍历集合就必须实现IEnumerable接口,而要实现IEnumerable接口就要实现IEnumerator接 口。关于如何实现这两个接口我们在第二部分会看到,在谈foreach的运行机制之前请允许我使用msdn中的Person类(我们下面的几部分中我们还 会用到相关的People和PeopleEnum类):
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace IteratorDemo { class Person { public Person(string fName, string lName) { this.firstName = fName; this.lastName = lName; } public string firstName; public string lastName; } }
当然具体细节我就不再说了,有了上面的Person类我们就可以运行下面的代码了:
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; namespace IteratorDemo { class Program { static void Main(string[] args) { Person[] persons = new Person[] { new Person("Kenshin","Cui"), new Person("Miaoer","Sun"), new Person("Jinjuan","Shen"), new Person("Yanxin","Nie") }; foreach (Person p in persons) { Console.WriteLine(p.firstName + " " + p.lastName); } Console.Read(); } } }
具体的运行结果也没有什么可说的,可是为什么会有这样的结果呢?原因可以分两层来解释:第一就是我们的Persons是使用[]符号声明,这是一个 Array类的记号。而Array类实现了IEnumerable接口中GetEnumerator()方法,因此它可以使用foreach进行迭代;第 二,之所以实现IEnumerable接口的GetEnumerator()方法就能够迭代是因为foreach将上面的代码解析成如下的形式:
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; namespace IteratorDemo { class Program { static void Main(string[] args) { Person[] persons = new Person[] { new Person("Kenshin","Cui"), new Person("Miaoer","Sun"), new Person("Jinjuan","Shen"), new Person("Yanxin","Nie") }; //等价的方法 IEnumerator enuerator = persons.GetEnumerator(); while (enuerator.MoveNext()) { Person p = enuerator.Current as Person; Console.WriteLine(p.firstName + " " + p.lastName); } Console.Read(); } } }
我们知道GetEnumerator()方法返回一个IEnumerator类型的接口,在IEnumerator接口中有一个Current属性来返回 当前元素,而其MoveNext()方法又可以移动到集合的下一个元素(有则返回true,无则返回false),如此反复就形成了对整个集合的迭代(具 体原理可以参见上面链接的内容)。
上面我们谈到Array类实现了IEnumerable接口中的GetEnumerator()方法,因此可以使用foreach来循环遍历,那么我们自己当然也同样可以实现相关接口。
People类:
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; namespace IteratorDemo { class People:IEnumerable { private Person[] _people; public People(Person[] pArray) { _people = new Person[pArray.Length]; for (int i = 0; i < pArray.Length; i++) { _people[i] = pArray[i]; } } public IEnumerator GetEnumerator() { return new PeopleEnum(_people); } } }