18. 枚举器和迭代器

目录

18.1 枚举器和可迭代枚举

18.2 IEumerator接口

IEnumerable接口

使用IEnumerable和IEnumerator的示例

18.3 泛型枚举接口

18.4 迭代器

18.4.1 迭代器块

18.4.2 使用迭代器来创建枚举器

 18.4.3 使用迭代器来创建可枚举类型

18.5 常见迭代器模式

 18.6 产生多个可枚举类型

 18.7 将迭代器作为属性

18.8 迭代器实质 


18.1 枚举器和可迭代枚举

使用foreach语句;当我们使用foreach语句时,这个语句为我们以此取出数组中的每一个元素,允许我们读取它的值。之所以可以这样是因为数组可以按需提供一个枚举器(enumeator)的对象。枚举器可以依次返回请求的数组中的元素。枚举器知道项的次序并且跟踪他在序列中的位置,然后返回请求当前项。

对于有枚举器的类型而言,必须有一个方法来获取它。获取一个对象枚举器的方法是调用对象的GetEnumerator方法。实现GetEnumerator方法的类型叫做可枚举类型(enumerable type或enumerable)。数组是可枚举类型。

18.2 IEumerator接口

实现了IEnumerator接口的枚举器包含三个成员:Current、MoveNext以及Reset。

  • Current是返回序列中当前位置项的属性。
  1. 他是只读属性
  2. 他返回object类型的引用,所以可以返回任何类型。
  • MoveNext是枚举器位置前进到集合中下一项的方法。它也返回布尔值,指示新的位置是有效位置还是已经超过了序列的尾部。
  1. 如果新的位置是有效的返回true;
  2. 如果新的位置是无效的(比如当前位置到达尾部)方法返回false;
  3. 枚举器的原始位置在序列中的第一项之前,因此MoveNext必须在第一次使用Current之前调;
  • Reset是吧位置重置为原始状态的方法。

有了集合的枚举器,我们就可以使用MoveNext和Cureent来模仿foreach循环遍历集合中的项。

18. 枚举器和迭代器_第1张图片

IEnumerable接口

可枚举类是指实现了IEnumerable接口的类。IEnumerable接口只有一个成员——GetEnumerator方法,它返回对象的枚举器。

18. 枚举器和迭代器_第2张图片 

使用IEnumerable和IEnumerator的示例

 18. 枚举器和迭代器_第3张图片

18. 枚举器和迭代器_第4张图片 

18.3 泛型枚举接口

目前我们描述的枚举接口都是非泛型版本。实际上,在大多数情况下你应该使用泛型版本IEnumerable和IEnumerator。 

  • 对于非泛型接口形式:
  1. IEnumerable接口的GetEnumerator方法返回实现IEnumerator枚举器类实例;
  2. 实现IEnumerator的类实现了Cureent属性,它返回object的引用,然后我们必须把它转化为实际类型的对象。
  • 对于泛型接口形式:
  1. IEnumerable接口的GetEnumerator方法返回实现IEnumerator枚举器的实例
  2. 实现IEnumerator的类实现了Curennt属性,它返回实际类型的对象,而不是object基类的引用,然后转化为实际类型。

非泛型接口的实现不是类型安全的,它返回的object类型引用,然后必须转换为实际类型。

而泛型接口的枚举器是类型安全的,它返回的实际类型的引用。如果要创建自己的可枚举类。应该实现这些泛型接口。

18. 枚举器和迭代器_第5张图片

18.4 迭代器

编译器为我们创建枚举器和可枚举类型,这种结构叫迭代器。我们可以把手动编码的可枚举类型和枚举器替换为迭代器生成的枚举器和迭代器。 

18. 枚举器和迭代器_第6张图片

 

18.4.1 迭代器块

迭代器块是一个或者多个yield语句代码块

  • 方法主体;
  • 访问器主体;
  • 运算符主体。

迭代器块与其他代码块不同。其他块包含的语句被当作是命令式的。也就是说,先执行代码块的第一个语句,然后执行后面的语句,最后控制离开块。

另一方面,迭代器块不是需要在同一时间执行的一串命令式命令,而是描述了希望编译器为我们创建的枚举器类的行为。为迭代器块中的代码描述了如何枚举元素。

迭代器块有两个特殊一语句。

  • yield return语句指定了序列中返回的下一项。
  • yield break语句指定了在序列中没有其他项。

18.4.2 使用迭代器来创建枚举器

18. 枚举器和迭代器_第7张图片

 18.4.3 使用迭代器来创建可枚举类型

18. 枚举器和迭代器_第8张图片

 18. 枚举器和迭代器_第9张图片

18.5 常见迭代器模式

  • 当我们实现返回枚举器的迭代器时,必须通过实现GetEnumerator来让类可枚举,它返回由迭代器返回的枚举器。
  • 如果我们在类中实现迭代器返回可枚举类型,我们可以让类实现GetEnumerator来让类本身可枚举,或不实现GetEnumerator,让类不可枚举,
  1. 如果是实现GetEnumerator,让他调用迭代器方法以获取自动生成的实现IEnumerable的类实例。然后,从GetEnumerable对象返回由GetEnumerator创建的枚举器
  2. 如果不实现GetEnumerator使类本身不可枚举,仍然可以使用由迭代器返回的可枚举类只需要直接调用迭代器方法·。 

18. 枚举器和迭代器_第10张图片

 18.6 产生多个可枚举类型

18. 枚举器和迭代器_第11张图片

18. 枚举器和迭代器_第12张图片 

 18.7 将迭代器作为属性

18. 枚举器和迭代器_第13张图片

18. 枚举器和迭代器_第14张图片 18. 枚举器和迭代器_第15张图片

18.8 迭代器实质 

  • 迭代器需要System。Collections.Generic命名空间,因此我们需要使用using指令引入它。
  • 在编译器生成的枚举器中,reset方法没有实现。而它是接口需要的方法,因此调用时总是抛出System。NotSupportedException异常。
  • Befor 首次调用MoveNext的初始状态。
  • Running 调用MoveNext后进入这个状态。在这个状态中,枚举器检测并设置下一项的位置。在遇到yield return 。yield break 或在迭代器体结束时,退出状态。
  • Suspended 状态机等待下次调用MoveNext的状态。
  • After 没有更多项可以枚举。

如果状态机在Before或Suspended状态时调用了MoveNext方法,就转到Running状态。在Running

状态中,它检测集合的下一项并设置位置。

如果有更多项,状态机会转入Suspended状态,如果没有更多项,它转入并保持After状态;

18. 枚举器和迭代器_第16张图片

 

你可能感兴趣的:(C#,开发语言,c#)