C#迭代器和接口IEnumerable,IEnumerator
一.迭代器
学过C++的都知道,迭代器是STL中的一个类,用来遍历容器。
vector<int>::iterator iter;
for (iter = v1.begin(); iter != v1.end(); iter++)
C#中迭代器也是用来遍历集合,本质上是一个类/接口(IEnumerator),可以解决在不知道集合以何种key存储的情况下,对集合进行遍历。
int[] nums = {1, 2, 3, 4, 5};
foreach(int i in nums)
{Console.WriteLine(i);
}
可以看到C#的迭代器i,没有初始值,结束条件,变化的情况;这是因为C#中的foreach隐藏了迭代器的复杂性,foreach是一种语法糖。可以用foreach遍历的集合必须实现接口IEnumerable,IEnumerator.
二.IEnumerator接口
public interface IEnumerator
{bool MoveNext();object Current { get; }void Reset();
}
MoveNext
将当前元素向前移动到下一个位置,如果集合没有更多元素,那么它会返回false。Current
返回当前位置元素。在取得第一个元素之前 必须先调用MoveNext
,即使空集合也支持该操作。Reset
作用就是当前位置移回起点,并允许再一次枚举集合。此方法一般并不建议使用,因为完全可以重新实例化一个枚举器。
三.IEnumerable接口
public interface IEnumerable:
{IEnumerator GetEnumerator();
}
通过GetEnumerator
返回枚举器,IEnumerable
可以看作IEnumerator
的提供者。
下面例子演示foreach是一种语法糖,本质是调用接口IEnumerator的MoveNext方法和Current属性
class MainClass{static void Main(){string s = "Hello";IEnumerator<char> iter = s.GetEnumerator(); //通过GetEnumerator方法获取迭代器while (iter.MoveNext()) //通过MoveNext方法判断是否还有下一个元素,并向后移动{char c = (char)iter.Current; //通过Current属性获取元素Console.Write(c + "_");}Console.WriteLine();foreach(char c in s){Console.Write(c + ".");}Console.WriteLine();}}/* Output:H_e_l_l_oH.e.l.l.o*/
四.遍历自定义集合的做法
下面的代码示例演示自定义集合的IEnumerable和IEnumerator接口的实现。 在此示例中,不会显式调用这些接口的成员,它们实现为支持使用foreach循环访问集合。
using System;
using System.Collections;// Simple business object.
public class Person
{public Person(string fName, string lName){this.firstName = fName;this.lastName = lName;}public string firstName;public string lastName;
}// Collection of Person objects. This class
// implements IEnumerable so that it can be used
// with ForEach syntax.
public 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];}}// Implementation for the GetEnumerator method.IEnumerator IEnumerable.GetEnumerator(){return (IEnumerator) GetEnumerator();}public PeopleEnum GetEnumerator(){return new PeopleEnum(_people);}
}// When you implement IEnumerable, you must also implement IEnumerator.
public class PeopleEnum : IEnumerator
{public Person[] _people;// Enumerators are positioned before the first element// until the first MoveNext() call.int position = -1;public PeopleEnum(Person[] list){_people = list;}public bool MoveNext(){position++;return (position < _people.Length);}public void Reset(){position = -1;}object IEnumerator.Current{get{return Current;}}public Person Current{get{try{return _people[position];}catch (IndexOutOfRangeException){throw new InvalidOperationException();}}}
}class App
{static void Main(){Person[] peopleArray = new Person[3]{new Person("John", "Smith"),new Person("Jim", "Johnson"),new Person("Sue", "Rabon"),};People peopleList = new People(peopleArray);foreach (Person p in peopleList)Console.WriteLine(p.firstName + " " + p.lastName);}
}/* This code produces output similar to the following:** John Smith* Jim Johnson* Sue Rabon**/
五.IEnumerable返回IEnumerator的作用
先说结论,如果集合实现IEnumerator的话,存在问题。
当出现循环嵌套时,内循环跳出时会把index制为-1,外循环永远无法结束,造成死循环。
为了解决这一问题,IEnumerable的GetEnumerator方法会返回一个new出来的IEnumerator对象