IEnumerable和IEnumerator类型:
IEnumerable只是一个接口用于foreach迭代器遍历之前用一个GetEnumerator获取一个IEnumerator对象且只获取一次,IEnumerator才是真正的迭代器对象,IEnumerator有Current属性,MoveNex()t和Reset()方法。所以如果是系统默认类型的IEnumerable<T>和IEnumerator<T>那么就不需要自定义这两个类了,反之则要定义。这个是一个迭代器模式,是C#广泛使用泛型和面向接口编程的经典体现。
1.foreach语句
C#中的foreach语句不会翻译为IL中的foreach语句,而是会翻译为IEnumerable中的接口的属性和函数,将类型替换为相应的泛型,IEnumerator<T> while MoveNext Current语句,会用while一次遍历迭代器的所有元素。含有数组成员的类会继承Array类,Array类会继承IEnumerable类,IEnumerable类包含了一个IEnumerator GetEnumerator() 函数,foreach语句会利用返回的IEnumerator的Current,MoveNext(),Reset()来循环处理函数。
例如:
foreach( var p in persons )
{
Console.WriteLine(p);
}
会被CRL编译为:
IEnumerator<Person> enumerator = persons.GetEnumerator();
while(enumerator.MoveNext())
{
Person p = enumerator.Current;
Console.WriteLine(p);
}
所以要声明支持foreach类型的自定义枚举集合类,那么需要声明数组默认继承IEnumerable需要实现IEnumerator<T> GetEnumerator()函数,再定义自己的IEnumerator<T>类,如果是默认类型的IEnumerator<T>就不需要了,这里只是用了迭代器设计模式和面向接口的编程思想,例如:
来自:https://msdn.microsoft.com/zh-cn/library/system.collections.ienumerable.getenumerator%28v=vs.110%29.aspx 的实例
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.
// People继承自IEnumerable,用于该类获取真正的迭代器
IEnumerator IEnumerable.GetEnumerator()
{
return (IEnumerator)GetEnumerator();
}
// PeopleEnum为真正的迭代器,因为PeopleEnum是自定义了,默认不支持
// 所以需要为PeopleEnum迭代器定义Current属性,和MoveNext,Reset方法。
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;
}
// MoveNext为false就不会继续移动了,除非reset或重新生成一个
public bool MoveNext()
{
position++;
return (position < _people.Length);
}
public void Reset()
{
position = -1;
}
// 真正的迭代器要求定义的属性,会调用Person Current
object IEnumerator.Current
{
get
{
return Current;
}
}
// 当前的获取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"),
};
// 自定义的IEnumerable类型
People peopleList = new People(peopleArray);
// 会得到自定义的PeopleEnum,接着进行循环遍历
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
*
*/
2.yield语句
yield语句也是一个迭代块,必须声明返回值类型为IEnumerable、IEnumerable<T>、IEnumerator<T>、IEnumerator类型,如果非内置类型的IEnumerable<T>还要定义IEnumerator<T> GetEnumerator(), 如果IEnumerator是非内置类型的那么需要定义真正迭代器的Current属性,和MoveNext,Reset方法。