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 中再谈,欢迎交流!

原文地址:https://www.cnblogs.com/ydchw/p/3735634.html