概念
迭代器是遍历容器的对象,尤其是列表
迭代器块
是有一个或多个yield语句的代码块
迭代器块语句是命令式,先执行代码块的第一个语句,然后执行后面的语句,最后控制离开块
特殊语句
yield return
语句指定了序列中返回的下一项yield break
语句指定在序列中没有其它项
使用迭代器来创建枚举器
class MyClass
{
//迭代器
public IEnumerator<string> BlackAndWhite()
{
yield return "black";
yield return "gray";
yield return "white";
}
//返回枚举器
public IEnumerator<string> GetEnumerator()
{
return BlackAndWhite();
}
}
class Program
{
static void Main(string[] args)
{
MyClass mc = new MyClass();
foreach(string shade in mc)
{
Console.WriteLine(shade);
}
Console.ReadKey();
}
}
结果
black
gray
white
使用迭代器来创建可枚举类型
class MyClass
{
//迭代器
public IEnumerable<string> BlackAndWhite()
{
yield return "black";
yield return "gray";
yield return "white";
}
//返回枚举器
public IEnumerator<string> GetEnumerator()
{
IEnumerable<string> myEnumerable = BlackAndWhite(); //获取可枚举类型
return myEnumerable.GetEnumerator(); //获取枚举器
}
}
class Program
{
static void Main(string[] args)
{
MyClass mc = new MyClass();
foreach(string shade in mc)
{
Console.WriteLine(shade);
}
foreach (string shade in mc.BlackAndWhite())
{
Console.WriteLine(shade);
}
Console.ReadKey();
}
}
结果
black
gray
white
black
gray
white
迭代器模式
- 当我们实现返回枚举器的迭代器时,必须通过实现GetEnumerator来让类可枚举,它返回由迭代器返回的枚举器。如上面(使用迭代器创建枚举器)代码
- 如果我们在类中实现迭代器返回可枚举类型,我们可以让类实现GetEnumerator来让类本身可被枚举,或不实现GetEnumerator,让类不可枚举。
- 如果实现GetEnumerator,让它调用迭代器方法以获取自动生成的实现IEnumerable的类实例。然后,从IEnumerable对象返回由GetEnumerator创建的枚举器,如上面(使用迭代器创建可枚举类型)代码
- 如果通过不实现GetEnumerator使类本身不可枚举,仍然可以使用由迭代器返回的可枚举类,只需要直接调用迭代器方法,如上面(使用迭代器创建可枚举类型)代码Main中第二个foreach
产生多个可枚举类型
class Spectrum
{
string[] Colors = { "violet", "blue", "cyan", "green", "yellow", "orange", "red" };
public IEnumerable<string> UVtoIR()
{
for(int i = 0; i < Colors.Length; i++)
{
yield return Colors[i];
}
}
public IEnumerable<string> IRtoUV()
{
for (int i = Colors.Length - 1; i >= 0; i--)
{
yield return Colors[i];
}
}
}
class Program
{
static void Main(string[] args)
{
Spectrum spectrum = new Spectrum();
foreach(string color in spectrum.UVtoIR())
{
Console.Write($"{color}, ");
}
Console.WriteLine();
foreach(string color in spectrum.IRtoUV())
{
Console.Write($"{color}, ");
}
Console.WriteLine();
Console.ReadKey();
}
}
结果
violet, blue, cyan, green, yellow, orange, red,
red, orange, yellow, green, cyan, blue, violet,
将迭代器作为属性
声明两个不同的迭代器
class Spectrum
{
bool _listFromUVtoIR;
string[] Colors = { "violet", "blue", "cyan", "green", "yellow", "orange", "red" };
public Spectrum(bool listFromUVtoIR)
{
_listFromUVtoIR = listFromUVtoIR;
}
public IEnumerator<string> UVtoIR
{
get
{
for(int i = 0; i < Colors.Length; i++)
{
yield return Colors[i];
}
}
}
public IEnumerator<string> IRtoUV
{
get
{
for (int i = Colors.Length - 1; i >= 0; i--)
{
yield return Colors[i];
}
}
}
public IEnumerator<string> GetEnumerator()
{
return _listFromUVtoIR ? UVtoIR : IRtoUV;
}
}
class Program
{
static void Main(string[] args)
{
Spectrum startUV = new Spectrum(true);
Spectrum startIR = new Spectrum(false);
foreach(string color in startUV)
{
Console.Write($"{color}, ");
}
Console.WriteLine();
foreach (string color in startIR)
{
Console.Write($"{color}, ");
}
Console.WriteLine();
Console.ReadKey();
}
}
迭代器实质
- 迭代器需要
System.Collections.Generic
命名空间,因此我们需要使用using
指令引入它。 - 在编译器生成的枚举器中,Reset方法没有实现。而它是接口需要的方法,因此调用时总是抛出System.NotSupportedException异常。
- 迭代器状态机
- Before首次调用MoveNext的初始状态。
- Running调用NoveNext后进入这个状态。在这个状态中,枚举器检测并设置下一项的位置。在遇到yield return、yield break或在迭代器体结束时,退出状态。
- Suspended状态机等待下次调用MoveNext的状态。
- After没有更多项可以枚举。
如果状态机在Before或Suspended状态时调用了MoveNext方法,就转到了Running状态。在Running状态中,它检测集合的下一项并设置位置。
如果有更多项,状态机会转入Suspenided状态,如果没有更多项,它转人并保持在After状态。