在面向对象编程中,迭代器模式是一种非常重要的行为型设计模式。它主要用于顺序访问集合对象中的元素,而无需暴露该集合的内部结构。在C#中,迭代器模式通过 IEnumerable
和 IEnumerator
接口实现,是实现 foreach
循环的基础。
本文将深入讲解常见的迭代器模式,包括如何创建可枚举类型、如何在类中实现多个迭代器方法,以及它们在实际开发中的应用方式。
迭代器模式的基本概念
什么是迭代器模式?
迭代器模式(Iterator Pattern)提供了一种访问聚合对象(如集合)元素的方式,而不暴露其内部结构。它将遍历逻辑封装在迭代器对象中,使得客户端代码可以一致地访问不同类型的聚合对象。
在 C# 中,迭代器的核心接口是:
IEnumerable
:表示一个可枚举的集合,提供GetEnumerator()
方法。IEnumerator
:表示集合的枚举器,提供MoveNext()
、Reset()
和Current
方法。
C# 中的迭代器实现方式
在 C# 中,迭代器可以通过两种方式实现:
**方法一:**返回 IEnumerator
的迭代器
public IEnumerator<string> GetEnumerator()
{
for (int i = 0; i < colors.Length; i++)
yield return colors[i];
}
这种方式适用于类本身是可枚举类型(即实现了 IEnumerable
接口),并返回一个 IEnumerator
。
**方法二:**返回 IEnumerable
的迭代器
public IEnumerable<string> UVtoIR()
{
for (int i = 0; i < colors.Length; i++)
yield return colors[i];
}
这种方式返回一个可枚举对象,但类本身不一定是可枚举的,客户端可通过调用该方法来获取迭代器。
图解常见迭代器模式的使用方式
以下图示来源于《C# 图解教程》第19章图19-11,展示了两种常见的迭代器使用方式。
类实现 GetEnumerator
成为可枚举类型
当一个类实现了 IEnumerable<T>
接口并重写了 GetEnumerator()
方法后,它就成为一个可枚举类型,可以被 foreach
遍历。
public class MyCollection : IEnumerable<string>
{
private string[] items = { "A", "B", "C" };
public IEnumerator<string> GetEnumerator()
{
return ((IEnumerable<string>)items).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
类不实现 GetEnumerator
,但提供多个可枚举方法
有时我们希望一个类具备多个遍历方式(如正序、逆序、筛选等),但又不希望类本身是可枚举的。这时我们可以通过定义多个返回 IEnumerable<T>
的方法来实现。
public class Spectrum
{
private 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];
}
}
调用方式如下:
Spectrum spectrum = new Spectrum();
Console.WriteLine("UV to IR:");
foreach (string color in spectrum.UVtoIR())
{
Console.WriteLine(color);
}
Console.WriteLine("IR to UV:");
foreach (string color in spectrum.IRtoUV())
{
Console.WriteLine(color);
}
这种方式非常适合实现多维遍历逻辑,例如:按时间顺序、按优先级、按区域等。
迭代器模式的优势与应用场景
优势分析
优势 | 描述 |
---|---|
封装性好 | 遍历逻辑与集合实现分离,降低耦合 |
扩展性强 | 可轻松添加新的遍历方式,如逆序、条件筛选等 |
支持延迟执行 | 使用 yield return 实现按需加载,节省资源 |
简化客户端代码 | 客户端只需使用 foreach ,无需关心内部结构 |
典型应用场景
- 数据集遍历:数据库查询结果、文件内容读取、网络数据流。
- 多维遍历需求:如顺序、逆序、按条件过滤、分页等。
- UI 控件绑定:WinForm、WPF、Blazor 中的数据绑定。
- LINQ 查询实现:LINQ 中的
Where
、Select
等方法都依赖迭代器模式。
使用技巧与注意事项
使用 yield return
简化迭代器实现
C# 提供了 yield return
语法糖,可以非常方便地生成迭代器,而不需要手动实现 IEnumerator
接口。
public IEnumerable<int> GetEvenNumbers()
{
for (int i = 0; i < 100; i++)
if (i % 2 == 0)
yield return i;
}
注意线程安全与状态同步
迭代器在执行过程中会保存当前状态,如果在多线程环境中使用,需要注意同步问题。
避免在迭代器中修改集合结构
在迭代过程中修改集合结构(如增删元素)会导致异常,应避免此类操作。
总结
迭代器模式是 C# 编程中非常常见且实用的设计模式。它不仅简化了集合的遍历操作,还提升了代码的可维护性和可扩展性。通过灵活使用 IEnumerable
和 IEnumerator
接口,我们可以轻松实现多种遍历方式,满足复杂业务需求。
如果你正在开发一个需要支持多种遍历逻辑的类,或者希望提高代码的封装性和可复用性,迭代器模式绝对是一个非常值得掌握的设计模式。