可枚举类型是实现IEnumerable接口的类型,
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
IEnumerable接口中有一个方法,用于返回枚举器,枚举器是实现了IEnumerator接口的类型。
IEnumerator接口定义如下:
//
// 摘要:
// 支持对非泛型集合的简单迭代。
[ComVisible(true)]
[Guid("496B0ABF-CDEE-11d3-88E8-00902754C43A")]
public interface IEnumerator
{
//
// 摘要:
// 获取集合中位于枚举数当前位置的元素。
//
// 返回结果:
// 集合中位于枚举数当前位置的元素。
object Current { get; }
//
// 摘要:
// 将枚举数推进到集合的下一个元素。
//
// 返回结果:
// 如果枚举数已成功地推进到下一个元素,则为 true;如果枚举数传递到集合的末尾,则为 false。
//
// 异常:
// T:System.InvalidOperationException:
// 集合在枚举器创建后被修改。
bool MoveNext();
//
// 摘要:
// 将枚举数设置为其初始位置,该位置位于集合中第一个元素之前。
//
// 异常:
// T:System.InvalidOperationException:
// 集合在枚举器创建后被修改。
void Reset();
}
///
/// 自定义一个Colors类,实现IEnumbale接口
///
public class Colors: IEnumerable
{
string[] colors = new string[] { "Red", "Yellow", "Blue", "Green", "orange" };
///
/// 接口的实现,这里需要返回一个枚举器。
/// 理论上需要自己实现一个枚举器(实现IEnumerator接口),
/// 因为这里我们的string[]的父类是Array,它已经实现了IEnumerable接口。所以它自己本身是有一个枚举器的。
///
/// 返回一个枚举器
public IEnumerator GetEnumerator()
{
return colors.GetEnumerator();
}
}
上述代码中,我们实现了IEnumerable接口,那么它就是一个可枚举类型,则可以使用foreach进行枚举。
static void Main(string[] args)
{
Colors colors = new Colors();
foreach (var item in colors)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
但是,只要一个类中有一个public的IEnumerator GetEnumerator()的方法,那么它就是一个可枚举的类型,并不一定需要实现IEnumerable接口,那么意味Colors类可以改写为这样子:
///
/// 自定义一个Colors类,实现IEnumbale接口
///
public class Colors
{
string[] colors = new string[] { "Red", "Yellow", "Blue", "Green", "orange" };
///
/// 接口的实现,这里需要返回一个枚举器。
/// 理论上需要自己实现一个枚举器(实现IEnumerator接口),
/// 因为这里我们的string[]的父类是Array,它已经实现了IEnumerable接口。所以它自己本身是有一个枚举器的。
///
/// 返回一个枚举器
public IEnumerator GetEnumerator()
{
return colors.GetEnumerator();
}
}
那么,对于可枚举类型的定义可以改为:实现IEnumerator GetEnumerator()方法的类型叫做可枚举类型。
foreach (Type varName in 可枚举类型)
{
}
在foreach中我们实际需要的是一个可枚举类型,即实现了IEnumerable接口的类型,这个接口里面有一个枚举器,从枚举器中对每一项进行的迭代。例如Array类型就已经实现了IEnumerable接口。
1)IEnumerable接口的GetEnumerator()方法返回实现IEnumerator枚举器类的实例。
2)实现IEnumerator的类的实现了Current属性,它是一个object类型的,然后我们必须把它转换为实际类型的类型。
3)IEnumerable泛型接口中GetEnumerator()方法返回的也是一个泛型枚举器类型IEnumerator。在
IEnumerator中Current也是一个T类型的,而不是object类型。
迭代器可以简单看作返回IEnumerable或者IEnumerator类型的方法,该方法中使用了两种特殊的语句:
1)yield return,指定了序列中返回的下一项
2)yield break,指定在序列中没有其他项
迭代器的意义就是把手动编码(这里应该值的是实现接口的方式)的可枚举类型和枚举器替换为有迭代器生产的可枚举类型和枚举器。
public class Colors
{
///
/// 实现了GetEnumerator()方法,把Colors类定义为可枚举类型
///
/// 返回一个枚举器
public IEnumerator GetEnumerator()
{
return this.GetColors();//这里的枚举器是使用迭代器船创建的
}
public IEnumerator<string> GetColors()
{
yield return "red";
yield return "yellow";
yield return "blue";
yield return "green";
yield return "orange";
}
}
其中迭代器改成这样也是可以的:
public IEnumerator<string> GetColors()
{
string[] colors = new string[] { "red", "yellow", "blue", "green", "orange" };
for (int i = 0; i < colors.Length; i++)
{
yield return colors[i];
}
//yield return "red";
//yield return "yellow";
//yield return "blue";
//yield return "green";
//yield return "orange";
}
再来看看yield break和yield return null的作用:
public IEnumerator<string> GetColors()
{
//string[] colors = new string[] { "red", "yellow", "blue", "green", "orange" };
//for (int i = 0; i < colors.Length; i++)
//{
// if (i == 1)
// yield break;
// yield return colors[i];
//}
yield return "red";
yield return "yellow";
yield return null;
yield return "blue";
yield break;
yield return "green";
yield return "orange";
}
当遇到yield return null的时候吗,还是会返回null,并且后面的项会继续返回。
当遇到yield break的时候,则会直接跳出,后面的项也不会返回。
直接上代码:
public class Colors
{
///
/// 实现了GetEnumerator()方法,把Colors类定义为可枚举类型
///
/// 返回一个枚举器
public IEnumerator GetEnumerator()
{
IEnumerable<string> e = this.GetColors();
return e.GetEnumerator();//这里的枚举器是通过定义好的可枚举类型的GetEnumerator()得到的
}
///
/// 使用迭代器创建了一个可枚举类型
///
///
public IEnumerable<string> GetColors()
{
string[] colors = new string[] { "red", "yellow", "blue", "green", "orange" };
for (int i = 0; i < colors.Length; i++)
{
yield return colors[i];
}
//yield return "red";
//yield return "yellow";
//yield return "blue";
//yield return "green";
//yield return "orange";
}
}
我们可以看到,在迭代器中的代码几乎相同,只是把返回值的类型改为了可枚举类型。然后在GetEnumerator方法中将这个可枚举类型的枚举器返回(也是通过GetEnumerator方法)。对于可枚举类型来说,肯定会有一个GetEnumerator方法用于返回它的枚举器。
其实,如果我们已经在类中实现了迭代器返回一个可枚举类型,那么就没有必要让这个类本身在可枚举了,也就是说没有必要再实现GetEnumerator()方法了,可简化为如下代码:
public class Colors
{
///
/// 使用迭代器创建了一个可枚举类型
///
///
public IEnumerable<string> GetColors()
{
string[] colors = new string[] { "red", "yellow", "blue", "green", "orange" };
for (int i = 0; i < colors.Length; i++)
{
yield return colors[i];
}
//yield return "red";
//yield return "yellow";
//yield return "blue";
//yield return "green";
//yield return "orange";
}
}
使用的时候就需要这样:
static void Main(string[] args)
{
Colors colors = new Colors();
foreach (var item in colors.GetColors())
{
Console.WriteLine(item);
}
Console.ReadKey();
}
1、当我们实现返回枚举器的迭代器时,必须通过GetEnumerator方法来让类可枚举,它返回由迭代器返回的枚举器。
2、如果我们在类中实现迭代器返回可枚举类型,我们可以让类实现GetEnumerator来让类本身可被枚举;或不实现GetEnumerator,让类不可枚举,只需要直接调用迭代器方法进行foreach枚举。
public class Colors
{
string[] colors = new string[] { "red", "yellow", "blue", "green", "orange" };
///
/// 使用迭代器创建了一个可枚举类型
///
///
public IEnumerable<string> GetColorsOne()
{
for (int i = 0; i < colors.Length; i++)
{
yield return colors[i];
}
}
public IEnumerable<string> GetColorsTwo()
{
for (int i = colors.Length - 1; i >= 0; i--)
{
yield return colors[i];
}
}
}
class Program
{
static void Main(string[] args)
{
Colors colors = new Colors();
foreach (var item in colors.GetColorsOne())
{
Console.WriteLine(item);
}
Console.WriteLine("=========");
foreach (var item in colors.GetColorsTwo())
{
Console.WriteLine(item);
}
Console.ReadKey();
}
}
public enum EnumeratorType
{
One,
Two,
}
public class Colors
{
EnumeratorType type;
public Colors(EnumeratorType type)
{
this.type = type;
}
public IEnumerator<string> GetEnumerator()
{
switch (this.type)
{
case EnumeratorType.One:
default:
return this.GetColorsOne;
case EnumeratorType.Two:
return this.GetColorsTwo;
}
}
string[] colors = new string[] { "red", "yellow", "blue", "green", "orange" };
///
/// 使用迭代器创建了一个可枚举类型
///
///
public IEnumerator<string> GetColorsOne
{
get
{
for (int i = 0; i < colors.Length; i++)
{
yield return colors[i];
}
}
}
public IEnumerator<string> GetColorsTwo
{
get
{
for (int i = colors.Length - 1; i >= 0; i--)
{
yield return colors[i];
}
}
}
}
class Program
{
static void Main(string[] args)
{
Colors colors = new Colors(EnumeratorType.One);
foreach (var item in colors)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
}