您的位置:首页 > 其它

foreach/yield语句自定义IEnumerable和IEnumerator类型

2015-10-02 09:33 465 查看
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方法。

  这样yield语句会生成一个枚举器IEnumerator,CRL翻译为Switch语句的MoveNext、Current语句、State保持当前的语句下一个索引状态的状态迭代器,下次重入的时候就从迭代器的下一个索引取数据并置下一个状态,就马上返回,就是一个很简单程序设置没有什么高级的分段执行机制,只是在多线程异步事件或者协程事件中能够发挥强大的作用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: