迭代器

首先搞清楚这两个接口,可以看出这两个接口是有一定的关联的,IEnumerable的接口成员是返回一个IEnumerator接口对象,

这个接口对象,有三个成员。

// 摘要:

// Exposes an enumerator, which supports a simple iteration over a non-generic

// collection.To browse the .NET Framework source code for this type, see the

// Reference Source.

[ComVisible(true)]

[Guid("496B0ABE-CDEE-11d3-88E8-00902754C43A")]

public interface IEnumerable

{

// 摘要:

// Returns an enumerator that iterates through a collection.

//

// 返回结果:

// An System.Collections.IEnumerator object that can be used to iterate through

// the collection.

[DispId(-4)]

IEnumerator GetEnumerator();

}

 

// 摘要:

// Supports a simple iteration over a non-generic collection.

[ComVisible(true)]

[Guid("496B0ABF-CDEE-11d3-88E8-00902754C43A")]

public interface IEnumerator

{

// 摘要:

// Gets the current element in the collection.

//

// 返回结果:

// The current element in the collection.

object Current { get; }

 

// 摘要:

// Advances the enumerator to the next element of the collection.

//

// 返回结果:

// true if the enumerator was successfully advanced to the next element; false

// if the enumerator has passed the end of the collection.

//

// 异常:

// System.InvalidOperationException:

// The collection was modified after the enumerator was created.

bool MoveNext();

//

// 摘要:

// Sets the enumerator to its initial position, which is before the first element

// in the collection.

//

// 异常:

// System.InvalidOperationException:

// The collection was modified after the enumerator was created.

void Reset();

}

 

这里首先还要区分两个概念,一个是调用这个迭代器,一个是实现这个迭代器,以前傻傻分不清楚。

先说调用这个迭代器,我们最常见的形式是使用foreach,编译器会将foreach编译来调用GetEnumeratorMoveNextCurrent

所以一般我们有两种方法来调用,本质是一样的:

再看看如何实现这个迭代器:

C#1的时候:

我们需要手工实现这个IEnumerable接口,为了实现接口,我们需要实现IEnumerator接口,生成一个内部类,然后再返回一个这个对象实例即可

我们发现,为了实现一个迭代器,我们需要做很多的工作,但是我们能看到具体的细节,可以看出,如果我们调用迭代器的时候,就跟调用方法一样,每调用一次,返回一个结果。

 

C#2.0的时候,实现迭代器,因为以前实现迭代器太繁琐,所以编译器打算自己跟我们把这些工作做好:

我们发现,简单了很多,不需要自己写内部类等,只需要特殊的关键字yield即可。

 

疑难问题:

1)书上说,迭代一个类,可使用方法GetEnumerator(),返回类型为IEnumerator,迭代一个类成员,则返回IEnumerable

迭代一个类,自然要实现IEnumerable接口,这个接口的成员就是GetEnumerator,自然就应该返回IEnumerator

迭代一个类成员,因为这个成员方法不会像类一样去实现什么接口,所以肯定要返回IEnumerable对象,所以内部也必须要能够帮助完成这一点,

要么有这么一个实现了这个接口的子类,要么使用yield,本质上一样的,因为只有IEnumerable才能被迭代,IEnumerator不能迭代。

 

2)不能在迭代块中写任何在方法调用时需要立即执行的代码-比如说参数验证。

这点可以这样来理解,因为在正式迭代之前,也就是调用MoveNext()方法之前,yield语句等才开始调用,所以就相当于即需加载一样。

 

3)关于finally块执行流程,其实很简单,如果是通过foreach来调用的,那么在yield break的时候,就会执行finally语句块,如果手动调用movenext()方法,则不会自动调用,

需要手工调用dispose方法,释放资源,这里IEnumerator跟其泛型是不一样的,IEnumerator<T>继承了IDisposable接口。迭代块里面的finally中的语句,就是最终Dispose方法

应该调用的地方,所以就解释了之前的疑惑:在迭代完成后会释放迭代器,这里是针对方法返回IEnumerable<T>的情况,如果是 类应该也是一样,也可以加finally吧,比如读取IO

资源的时候,在这里可以进行释放。

 

4)不能理解迭代器的优点在哪。

举一点例子,如果是查询文件,找到所有行,如果调用File.ReadAllLines,以及实现一个迭代器Array.FindAll, 后者在文件较大的时候就优势体现出来了,

后者是流线型处理的,一行一行读过来。

访问一个聚合对象的内容而无需暴露它的内部表示,所以可以提供聚合对象的多种遍历。

 

https://files.cnblogs.com/files/monkeyZhong/NewOverrideDemo.zip

 

 

 

 

posted @ 2018-01-18 14:18  MonkeyZhong  阅读(671)  评论(0编辑  收藏  举报