版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_38801354/article/details/79522535
1 枚举器
为什么数组可以使用 foreach
输出各元素?
- 这是因为数组是 枚举类型(enumerable) ,它通过
GetEnumerator
方法提供一个 枚举器(enumerator),即能实现GetEnumerator
方法的类型是 枚举类型 - 枚举器可以依次返回请求的数组的元素,枚举器知道 项的次序 并跟踪它的序列位置,然后返回 请求的当前项
- 对象枚举类型获取枚举器的方法是调用对象的
GetEnumerator
方法
1.1 Ienumerable
接口
可枚举类可以是 实现 Ienumerable
接口的类型,而 Ienumberable
接口只有一个成员,那就是 GetEnumerator
方法,它返回对象的 枚举器
using System.Collections;
// 实现 Ienumerable 接口
class MyColors: IEnumerable
{
string[] Colors = {"Red", "Yellow", "Blue"};
public IEnumerator GetEnumerator()
{
return new ColorEnumerator(); // 注意上方返回的类型是 IEnumerator,所以这里的返回对象引用会自动转换为它们实现的接口引用
}
}
1.2 Ienumerator
接口
实现 Ienumerator
接口的枚举器,它包含三个方法:
Current
:返回序列中当前位置项,属于一个只读 属性MoveNext
:把枚举器的位置移动到集合中下一项的方法,返回一个布尔值,有下一项返回true
;另外 枚举器的原始位置在第一项之前(如-1
),即在使用Current
之前,必须要先调用MoveNext
方法Reset
:把枚举器的位置重置到初始状态的方法
至此,我们也可以写出类如 foreach
语句的功能,如:
static void Main()
{
int[] MyArray = {10, 11, 12, 13};
IEnumerator ie = MyArray.GetEnumerator(); // 实现接口,对象引用能自动转换为它们实现的接口引用
while (ie.MoveNext())
{
int i = (int) ie.Current;
Console.WriteLine("{0}", i);
}
}
使用 IEnumerable
和 IEnumerator
的实例
using System;
using System.Collections;
// 创建枚举器
class ColorEnumerator : IEnumerator
{
string[] _colors; // 用于存储内部的 color 数组
int _position = -1; // 用于表示枚举器位置
// 构造函数,用于创建 _color 数组
public ColorEnumerator(string[] theColors)
{
_colors = new string[theColors.Length];
for (int i = 0; i < theColors.Length; i++)
{
_colors[i] = theColors[i];
}
}
// 实现 Current
public object Current
{
get
{
if (_position == -1)
{
throw new InvalidOperationException();
}
if (_position >= _colors.Length)
{
throw new InvalidOperationException();
}
return _colors[_position];
}
}
// 实现MoveNext
public bool MoveNext()
{
if (_position < _colors.Length - 1)
{
_position++;
return true;
}
else
{
return false;
}
}
// 实现 Reset
public void Reset()
{
_position = -1;
}
}
// 枚举类型,实现 IEnumrable 接口,提供 GetEnumerator
class Sectrum : IEnumerable
{
string[] Colors = { "violet", "blue", "cyan", "green" };
public IEnumerator GetEnumerator()
{
return new ColorEnumerator(Colors);
}
}
// 主流图
class Program
{
static void Main()
{
Sectrum spectrum = new Sectrum();
foreach (string color in spectrum)
{
Console.WriteLine(color);
}
}
}
1.3 泛型枚举接口
在大多数情况下,都应该使用泛型版本的 IEnumerator<T>
和 IEnumerable<T>
,泛型与非泛型二者的本质都差不多。
对于非泛型而言:
IEnumerable
接口的GetEnumerator
方法返回实现IEnumerator
枚举类的实例- 实现
IEnumerator
的类实现了Current
属性,它返回object
的引用,然后我们必须把它转换为实际类型的对象
对于泛型:
IEnumerable<T>
接口的GetEnumerator
方法返回实现IEnumator<T>
枚举器类的实例- 实现
IEnumerator<T>
的类实现了Current
属性,它返回 实际类型的对象,而不是object
基类的引用
2 迭代器
C# 在2.0版本开始提供更简单的创建 枚举类型 和 枚举器 的方式,而实际上是 编译器为我们创建它们,这种结构叫做 迭代器,例如:
// 迭代器返回一个泛型枚举器
// 注意由迭代器返回的枚举器并不能完全实现 IEnumerator 接口
// 它没有实现 Reset 方法
public IEnumerator<string> BlackAndWhite()
{
yield return "black";
yield return "gray";
yield return "white";
}
2.1 迭代器块
迭代器块 是有一个或多个 yield
语句的 代码块,下面任意一种都可以是 迭代器块:
- 方法主体
- 访问器主体
- 运算符主体
迭代器块 与 其他代码块 不一样:
- 其他代码块 包含的语句被当做是 命令式,即先执行代码块的第一个语句,然后执行后面的语句,最后控制离开块
- 迭代器块 不是 需要在同一时间执行的一串命令式命令,而是 描述了希望编译器为我们创建的 枚举器 或 枚举类 的行为
yield return
语句指定序列中返回的下一项yield break
语句指定在序列中 没有 其他项
2.2 使用迭代器来创建枚举器
// 并没有实现 IEnumerable 接口
class MyClass
{
// 实现 GetEnumerator 方法
public IEnumerator<string> GetNnumerator()
{
return BlackAndWhite(); // 返回迭代器生成的泛型枚举器
}
// 迭代器返回泛型枚举器(字符串)
public IEnumerator<string> BlackAndWhite()
{
yield return "black";
yield return "gray";
yield return "white";
}
}
class Program
{
static void Main()
{
Myclass mc = new MyClass();
foreach (string shade in mc)
{
Console.WriteLine(shade);
}
}
}
其中要说明的是
foreach
只要实现了GetEnumerator
方法就可以使用foreach
方法进行遍历- 代码中的迭代器产生了一个枚举器,这个枚举器(
Enumerator
)有一个 嵌套类 实现了IEnumerator<string>
2.3 使用迭代器来创建可枚举类型
class MyClass
{
public IEnumerator<string> GetEnumerator()
{
IEnumerable<string> myEnumerable = BlackAndWhite();
return myEnumerable.GetEnumerator();
}
public IEnumerable<string> BlackAndWhite()
{
yield return "black";
yield return "gray";
yield return "white";
}
}
class Program
{
static void Main()
{
MyClass mc = new MyClass();
// 使用类对象
foreach (string shade in mc)
{
Console.Write("{0}", shade);
}
// 使用类方法
foreach (string shade in mc.BlackAndWhite())
{
Console.Write("{0}", shade);
}
}
}
其中编译器生成的类实现了 IEnumerator<string>
也实现了 IEnumerable<string>
2.4 迭代器的实质
- 迭代器需要
System.Collection.Generic
命名空间,因此我们需要使用using
指令引入它 - 在编译器生成枚举器中,
Reset
方法没有实现,而它是接口需要的方法