声明一个有6个元素的数组,我们可以使用foreach
来循环打印出数组中的各项的值
那么为什么数组可以这样做?原因是数组可以按需提供一个叫做 枚举器 的对象
枚举器可以一次返回请求的数组中的元素。枚举器 “知道” 项的次序并且跟踪他在序列中的位置然后返回请求的当前项
对于枚举器的类型而言,必须有一个方法来获取它。获取一个对象枚举器当方法是调用对象的GitEnumerator
方法。实现 GitEnumerator 方法的类型叫做 可枚举类型 数组是可枚举类型
下图描述了 可枚举类型和枚举器之间的关系
foreach
结构设计用来和可枚举类型一起使用。只要给它的遍历对象是可枚举类型,比如数组,它就会执行如下行为:
GetEnumerator
方法获取对象的枚举器// 必须是可枚举类型
foreach( Type VarNase in EnumerableObject )
{
...
}
实现了IEnumerator
接口的枚举器包含3个函数成员:Current、MoveNext 以及 Reset
Current
是返回序列中当前位置项的属性
true
false
MoveNext
必须在第一次使用Current
之前调用Reset
是把位置重置为原始状态的方法下图描述了 小集合的枚举器
枚举器于序列中的当前项保持联系的方式完全取决于实现。可以通过对象引用、索引值或其他方式来实现。对于内置的一维数组来说,就使用项的索引
可枚举类是指实现了IEnumerable
接口的类。IEnumerable
接口只有一个成员——GetEnumerator
方法,它返回对象的枚举器
枚举类型的声明形式
using System.Collections;
// 实现IEnumerable接口
class MyClass :IEnumerable
{
// 返回IEnumerator类型的对象
public IEnumerator GetEnumerator { ... }
...
}
模拟 ColorIEnumerator 的实现
using System.Collections;
class MyColors:IEnumerable
{
string[] Colors = { "Red", "Yellow", "Blue" };
public IEnumerator GetEnumerator()
{
// 枚举器类的实例
return new ColorEnumerator(Colors);
}
}
使用 IEnumerable 和 IEnumerator 的示例
using System;
using System.Collections;
class ColorEnumerator :IEnumerator
{
string[] _colors;
int _position = -1;
public ColorEnumerator( string[] theColors ) // 构造函数
{
colors = new string[theColors.Length];
for (int i = 0;i < theColors.Length; i++ )
_colors[i] = theColors[i];
}
public object Current // 实现Current
{
get
{
if (_position == -1 )
throw new InvalidOperationException();
if (_position >= _colors.Length )
throw new InvalidOperationException();
return _colors[_position];
}
}
public bool MoveNext() // 实现MoveNext
{
if ( _position < _colors.Length - 1 )
{
_position ++;
return true;
}
else
return false;
}
public void Reset() // 实现Reset
{
_position = -1;
}
}
class Spectrum : IEnumerable
{
string[] Colors = { "violet", "blue", "cyan", "green","yellow", "orange", "red" };
public IEnumerator CetEnumerator()
{
return new ColorEnumerator( Colors );
}
}
class Program
{
static void Main()
{
Spectrum spectrum = new Spectrum();
foreach( string color in spectrum )
Console.Writeline( color );
}
}
枚举接口的非泛型和泛型的本质差别
IEnumerable
接口的GetEnumerator
方法返回实现IEnumerator
枚举器类的实例IEnumerator
的类实现了Current
属性,它返回object
的引用,然后我们必须把它转化为实际类型的对象IEnumerable
接口的GetEnumerator
方法返回实现IEnumator
的枚举器类的实例IEnumerator
的类实现了Current
属性,它返回实际类型的对象,而不是object
基类的引用 自 C#2.0 版本开始提供了跟简单的创建枚举器和可枚举类型的方式,实际上,编译器将为我们创建它们,这种结构叫做迭代器
我们可以把手动编码的可枚举类型和枚举器替换为有迭代器生成的可枚举类型和枚举器
3中迭代器块:
方法主体
访问器主体
运算符主体
迭代器块与其他代码块不同。其他块包含的语句被当作是命令式的。也就是说,先执行代码块的第一个语句,然后执行后面的语句,最后控制离开块
另一方面,迭代器块不是需要在同一时间执行的一串命令式命令,而是描述了希望编译器为我们创建的枚举器类的行为。迭代器块中的代码描述了如何枚举元素
GetEnumerator
来让类可枚举,它返回由迭代器返回的枚举器GetEnumerator
来让类本身可被枚举,或不实现GetEnumerator
,让类不可枚举
CetEnumerator
,让它调用迭代器方法以获取自动生成的实现IEnumerable
的类实例。然后,从IEnumerable
对象返回由GetEnumerator
创建的枚举器GetEnumerator
使类本身不可枚举,仍然可以使用由迭代器返回的可枚举类,只需要直接调用迭代器方法using System;
using System.Collections.Generic;
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> IRtoU()
{
for ( int i = colors.Length -1; i >= 0; i--)
yleld return colors[i];
}
}
class Program
{
static void Main()
{
Spectrum spectrum = new Spectrum();
foreach ( string color in spectrum.UVtoIR())
Console.Write("{0}",color );
Console.WriteLine();
foreach ( string color in spectrum.IRtoUV())
Console.Write("{O}",color );
Console.Writeline();
}
}
using System;
using System.Collections.Generic;
class Spectrum
{
bool _listFromUVtoIR;
string[] colors = { "violet", "blue", "cyan", "green","yellow", "orange", "red");
public Spectrum( bool listFromVtoIR )
{
_listFromlVtoIR = listFromUVtoIR;
}
public IEnumerator<string> GetEnumerator()
{
return _listFromlVtoIR ? UVtoIR : IRtoUV;
}
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];
}
}
}
class Program
{
static void Main()
{
Spectrum startUV = new Spectrum( true );
Spectrum startIR = new Spectrum( false );
foreach ( string color in startuV )
Console.Write("{0}", color );
Console.Writeline();
foreach ( string color in startIR )
Console.Write("{0}", color );
Console.WriteLine();
}
}
System.Collections.Generic
命名空间,因此我们需要使用using指令引入它Reset
方法没有实现。而它是接口需要的方法,因此调用时总是抛出System.NotSupportedException
异常Before
首次调用MoveNext
的初始状态Running
调用MoveNext
后进入这个状态。在这个状态中,枚举器检测并设置下一项的位置。在遇到yield return、yield break
或 在迭代器体结束时,退出状态Suspended
状态机等待下次调用MoveNext
的状态After
没有更多项可以枚举Before
或Suspended
状态时调用了MoveNext
方法,就转到了Running
状态。在Running
状态中,它检测集合的下一项并设置位置