C#枚举器和迭代器
原创
©著作权归作者所有:来自51CTO博客作者步_步_为营的原创作品,请联系作者获取转载授权,否则将追究法律责任
C#枚举器和迭代器
使用foreach语句时,可以依次取出数组里面的元素,原因就是数组提供了“枚举器(Enumerator)”,枚举器知道元素的位置并返回请求项。
枚举器IEnumerator
枚举器实现了IEnumerator
接口,该接口中有Current属性、MoveNext和Reset方法,foreach实现原理类似如下代码:
static void Main(string[] args)
{
int[] MyArray = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
//可枚举类实现了GetEnumerator方法,获取枚举器
IEnumerator ie = MyArray.GetEnumerator();
while ( ie.MoveNext())
{
int i = (int)ie.Current;
Console.WriteLine(i);
}
}
示例
- 定义一个可枚举类
class Colors : IEnumerable
{
string[] colors = { "blue", "red", "yellow" };
public IEnumerator GetEnumerator()
{
return new ColorEnumerator(colors);
}
}
- 定义可枚举类中使用到的枚举器
class ColorEnumerator : IEnumerator
{
string[] colors;
int positon = -1;
public ColorEnumerator(string[] colors)
{
this.colors = colors;
for (int i = 0; i < colors.Length; i++)
{
this.colors[i] = colors[i];
}
}
public object Current
{
get
{
if (positon == -1 || positon >= colors.Length) throw new InvalidOperationException();
return colors[positon];
}
}
public bool MoveNext()
{
if(positon <colors.Length-1)
{
positon++;
return true;
}
else
{
return false;
}
}
public void Reset()
{
positon = -1;
}
}
- 使用可枚举类
static void Main(string[] args)
{
Colors colors = new Colors();
foreach (var item in colors)
{
Console.WriteLine(item);
}
Console.Read();
}
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n0cvT8KQ-1672824371803)(C#枚举器和迭代器.assets/image-20230104164933735.png)] C#枚举器和迭代器_.net](https://s2.51cto.com/images/blog/202301/30160200_63d77978e3c8658971.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
泛型枚举器
IEnumerator<T>
泛型枚举器与普通枚举器类似,不同之处在于普通枚举器的Current
属性是Object类型,在取出是需要进行转化(看上面while代码块中的代码),而泛型可以直接返回指定的类型。
迭代器
迭代器使用
迭代器简化了可枚举器和可枚举类型的编码工作。yield return
表示依次返回枚举中的下一项。
如何要实现上面的实例,只需要实现可枚举类:
class Colors
{
string[] colors = { "blue", "red", "yellow" };
public IEnumerator GetEnumerator()
{
return GetColorsEnumerator();
}
//返回枚举器
public IEnumerator<string> GetColorsEnumerator()
{
yield return "blue";
yield return "red";
yield return "yellow";
}
}
结果与上面示例完全相同
常见迭代器模式
- 迭代器返回枚举器
class Colors
{
public IEnumerator GetEnumerator()
{
return GetColors();
}
//该出返回的是枚举器
public IEnumerator<string> GetColors()
{
yield return ..;
yield return ..;
...
}
}
Colors colors = new Colors();
foreach (var item in colors)
{
Console.WriteLine(item);
}
- 迭代器返回可枚举类型
class Colors
{
//可以选择不实现该方法,让Colors类变成不可枚举类
public IEnumerator GetEnumerator()
{
return GetColors().GetEnumerator();
}
//该出返回的是可枚举类型
public IEnumerable<string> GetColors()
{
yield return ..;
yield return ..;
...
}
}
Colors colors = new Colors();
//如果实现GetEnumerator()方法,则使用以下方法
foreach (var item in colors)
{
Console.WriteLine(item);
}
//如果没有实现GetEnumerator()方法,则使用以下方法
foreach (var item in colors.GetColors())
{
Console.WriteLine(item);
}
多个迭代器
上面讲的迭代器有两种模式,为什么需要第2种方式?答案时候第2中方式更加灵活,比如可以实现多个迭代器
class Colors
{
string[] colors = { "blue", "red", "yellow" };
public IEnumerable<string> A()
{
for (int i = 0; i < colors.Length; i++)
{
yield return colors[i];
}
}
public IEnumerable<string> B()
{
for (int i = colors.Length-1; i >=0; i--)
{
yield return colors[i];
}
}
}
使用
static void Main(string[] args)
{
Colors colors = new Colors();
foreach (var item in colors.A())
{
Console.WriteLine(item);
}
Console.WriteLine("--------------");
foreach (var item in colors.B())
{
Console.WriteLine(item);
}
Console.Read();
}
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sj2pmDOe-1672824371804)(C#枚举器和迭代器.assets/image-20230104172159795.png)] C#枚举器和迭代器_迭代器_02](https://s2.51cto.com/images/blog/202301/30160201_63d77979383e997362.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=/resize,m_fixed,w_1184)
迭代器作为属性
上面的迭代器是作为方法,可以将方法封装为属性,方便调用
class Colors
{
string[] colors = { "blue", "red", "yellow" };
public IEnumerable<string> A
{
get
{
for (int i = 0; i < colors.Length; i++)
{
yield return colors[i];
}
}
}
public IEnumerable<string> B
{
get
{
for (int i = colors.Length - 1; i >= 0; i--)
{
yield return colors[i];
}
}
}
}
使用方式基本相同,只不过是将方法调用改成了属性