C#基础—迭代器(Iterator)
1、引入迭代器
记得以前经常做到一些面试题,是关于要实现迭代器必须实现什么接口?其实,在C# 1.0里我们就经常用到foreach了,所以,只要支持foreach,那么这个类型就可以使用foreach 去遍历。那如何才能支持foreach 呢?
其实,答案都知道,要实现IEnumerable 或 IEnumerable<T>、IEnumerator 接口,因为foreach 需要迭代器的支持,而IEnumerable 接口中含有返回迭代器的 GetEnumerator 方法,对此,可以查看下MSDN 对 IEnumerable 的定义。所以,我们只要实现这个方法就可以使用foreach 来遍历我们需要的类型。
2、迭代器的使用
什么是迭代器? 简单的理解就是集合中的某个位置。我们先写个小Demo来认识下如何去实现一个自定义的迭代器:
1 //1.首先定义一个需要实现foreach遍历的对象,我们假设为要遍历一群人 3 class Person 5 { 7 public string FirstName; 11 public string LastName; 15 public Person(string firstName, string lastName) 17 { 19 this.FirstName = firstName; 21 this.LastName = lastName; 23 } 25 } 26 29 //2. 定义一个那个我们要遍历的一群人集合,我们把这个类型叫:PersonList 31 class PersonList : IEnumerable 33 { 35 //其实操作的是Person的数组 37 private Person[] _personList; 41 public PersonList(Person[] PersonList) 43 { 45 this._personList = PersonList; 47 } 48 51 /// <summary> 53 ///用来遍历PersionList类的索引器,类似类数组 55 /// </summary> 57 /// <param name="index"></param> 59 /// <returns></returns> 61 public Person this[int index] 63 { 65 get 67 { 69 return this._personList[index]; 71 } 73 } 74 77 /// <summary> 79 ///一般集合也都有一个Count,我们也实现下 81 /// </summary> 83 public int Count 85 { 87 get 89 { 91 return this._personList.Length; 93 } 95 } 96 99 /// <summary> 101 ///这个是返回迭代器的方法,必须要实现的 103 /// </summary> 105 /// <returns></returns> 107 public IEnumerator GetEnumerator() 109 { 111 return (new PersonIterator(this)); //把当前自定义的集合传给迭代器进行遍历功能的实现,并返回迭代器对象 113 } 115 } 116 119 //3.定义一个自定义的迭代器,我们假设叫 PersonIterator,实现迭代器需要实现 System.Collection.IEnumerator 接口 121 class PersonIterator : IEnumerator 123 { 125 //要遍历的自定义对象集合,传入迭代器进行遍历 127 private readonly PersonList _personList; 131 //当前位置的索引 133 private int _position = -1; 134 137 public PersonIterator(PersonList personList) 139 { 141 this._personList = personList; 143 } 144 147 #region自定义迭代器必须要实现的IEnumerator中的几个方法和属性 151 public object Current 153 { 155 get 157 { 159 try 161 { 163 return this._personList[this._position]; 165 } 167 catch(IndexOutOfRangeException) 169 { 171 throw new IndexOutOfRangeException(); 173 } 175 } 177 } 178 181 public bool MoveNext() 183 { 185 this._position++; 187 return (this._position < this._personList.Count); 189 } 190 193 public void Reset() 195 { 197 this._position = -1; 199 } 203 #endregion 207 } 208 209 210 211 class Program 213 { 215 static void Main(string[] args) 217 { 219 Person[] personArray = new Person[2] 221 { 223 new Person("三", "张"), 225 new Person("四", "李") 227 }; 231 //测试 233 PersonList pList = new PersonList(personArray); 235 foreach (Person p in pList) 237 { 239 Console.WriteLine("遍历的姓名: {0} {1}", p.FirstName, p.LastName); 241 } 242 245 Console.Read(); 247 } 249 }
程序输出:
遍历的姓名:三 张
遍历的姓名:四 李
至此,我们在梳理下实现的步骤:
(1).定义要遍历的对象 Person
(2).定义要遍历的对象集合类 PersonList 实现 IEnumerable 接口,主要实现 GetEnumerator()方法,以便于返回一个迭代器对象。
(3).定义一个自定义的迭代器类 PersonIterator 实现 IEnumerator 接口,主要实现该接口中定义的方法和属性。
整个迭代器的实现过程略微显得复杂,如果每次都这样实现,的确感觉不太理想,因此C# 2.0 为我们提供了语法糖:yield return。让我们来看看实现的过程有哪些简化的呢?由于篇幅原因,我只提供了修改的地方:
1 ///// <summary> 2 ///// 这个是返回迭代器的方法,必须要实现的 3 ///// </summary> 4 ///// <returns></returns> 5 //public IEnumerator GetEnumerator() 6 //{ 7 // return (new PersonIterator(this)); //把当前自定义的集合传给迭代器进行遍历功能的实现,并返回迭代器对象 8 //} 9 10 11 /// <summary> 12 ///这个是返回迭代器的方法,必须要实现的, C# 2.0 的yield return 13 /// </summary> 14 /// <returns></returns> 15 public IEnumerator GetEnumerator() 16 { 17 for (var i = 0; i < this._personList.Length; i++) 18 { 19 yield return this._personList[i]; 20 } 22 }
这样我们就省去了第三步去实现IEnumerator来构造一个迭代器类,但是编译器的工作还是不会少的,当编译器看到 yield return 时会自动为我们实现IEnumerator 接口,有兴趣的同仁可以查看下最后编译后的代码,在此不再敖述。
3、迭代器的执行过程
对于迭代器的执行过程,大家可以调试测试,这里直接引用下园子里大神的图片做个演示了:
至此,迭代器先谈这么多了,至于迭代器的延迟计算,我们到Linq 中再谈,欢迎交流!