根据定义 .net中的集合本质上是一个继承IEnumerable<T>或者IEnumberable。这个结构只有一个方法,要想实现对集合的遍历就必须实现这个方法===>IEnumerator GetEnumerator().
IEnumerator 则要求实现一个属性两个方法:
object Current { get; } //返回集合中当前元素
bool MoveNext(); //如果返回成功则迭代器成功游到下一个元素 失败则最后一个元素
void Reset();//将迭代器重置
举一个例子一个简单的代码:
int[] array = new int[] { 1, 65, 486, 4321, 3, 216 };
for (int temp = 0; temp < array.Length; temp++)
{
Console.WriteLine(array[temp]);
}
C#编译器会用CIL来创建一个等价的for循环
int[] array = new int[]{1,65,486,4321,3,216};
int[] array2 = array;
for (int i = 0; i < array2.Length; i++)
{
int temp = array2[i];
Console.WriteLine(temp);
}
注意:此例子中foreach会依赖Length属性和数组索引操作符[]
但是对于许多集合类,例如Stack<T>,Queue<T>以及Dictorynary<TKey,TValue>都不支持索引取元素。因此会有一种更常规的方式遍历元素集合===>迭代器模式。只要能知道第一个元素、下一个元素和最后一个元素,就不需要事先知道元素的总数,也不需要按照索引获取元素。
Queue<int> test = new Queue<int>();
foreach (int temp in test)
{
Console.WriteLine(temp);
}
等同于
Queue<int> test2 = new Queue<int>();
var enumerator = test2.GetEnumerator();
while (enumerator.MoveNext())
{
Console.WriteLine(enumerator.Current);
}
然而这并不是完整的,在退出循环之后,有时候需要对状态进行清理,为此IDisposable就出现了。最终生成的CIL的代码其实是包含finnally disposable的
注意:好玩的一点出来了,据说C#编译器不需要必须实现IEnumerable<T>或者IEnumerable.它貌似采用了一个"Duck Typing"的概念,即将一个对象传递给正期待一个特定类型的方法,而不是继承。也就是说你只要有上述方法就OK。
接下来试试自定义集合类:
public class BuildIntType :
IEnumerable<string>
{
public IEnumerator<string> GetEnumerator()
{
yield return "object1";
yield return "object2";
yield return "object3";
yield return "object4";
yield return "object5";
yield return "object6";
yield return "object7";
yield return "object8";
yield return "object9";
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
迭代器类似于函数,但是他返回不是一个值,而是生成yieId 一系列的值。迭代器由编译器转换成“状态机”来记录和跟踪当前位置,它还知道如何移动到下一位置。每当遇到yield return时,都会生成一个值,控制立即返回到请求数据项的调用者,然后当请求下一项目时,会紧接着下一个yield return语句之后执行。