理解并在 C# 中实现迭代器模式






4.82/5 (24投票s)
如何在 C# 中实现迭代器模式
引言
本文旨在解释迭代器模式,并在 C# 中对迭代器模式进行初步实现。本文面向初学者,不使用任何语言内置的迭代功能。
背景
在软件开发中,拥有一个对象集合是非常普遍的。如果我们有一个对象集合,那么我们可能还需要遍历这个集合。大多数语言都提供了对基本集合类型的遍历技术。C# 也包含一些特殊的容器类型,能够保存值的集合(例如:C# 中的 List
和 ArrayList
)。这些专门的容器也具有迭代的可能性。C# 容器类是迭代器模式如何实现的最佳示例。
注意:在本文中,我们将不使用任何这些技术。
如果我们想要了解这些迭代器对象的底层工作机制,那么我们可能需要首先理解迭代器模式。迭代器模式背后的思想是将实际的集合对象与遍历逻辑分离。这将使集合对象更轻量级,因为它不必处理所有与迭代相关的功能,并且从用户的角度来看,集合及其迭代方式之间存在清晰的分离。此外,用户不必担心跟踪已遍历的项数、剩余的项数以及是否检查边界条件,因为所有这些都在迭代器对象中完成(因为这些事情将取决于集合对象的底层结构和实现)。
GoF 将迭代器模式定义为“提供一种顺序访问聚合对象元素的方法,而无需暴露其底层表示”。为了可视化 GoF 设计(根据我们的实现略作修改)
Using the Code
在开始实现之前,让我们试着看看这个图中的每个类代表什么
IIterator
:这是一个接口,定义了访问和遍历元素的方法。MyIterator
:这是ConcreteIterator
,它将实现 Iterator 接口并跟踪聚合对象遍历中的当前位置。IAggregate
:这是一个接口,定义了创建Iterator
对象的方法。MyAggregate
:这是ConcreteAggregate
对象,即,真实的集合位于其中。这个类实现了IAggregate
创建接口。
创建迭代器接口
现在让我们尝试通过一次实现一个类来实现这个模式。 让我们从编写 IIterator
接口开始。此接口应提供访问和遍历集合对象元素的方法。
IIterator
的实现
interface IIterator
{
string FirstItem { get;}
string NextItem{ get;}
string CurrentItem{ get;}
bool IsDone { get;}
}
创建聚合(集合)对象的接口
一旦我们准备好 IIterator
,就让我们拥有接口 IAggregate
。 这将简单地包含创建迭代器的方法。
IAggregate
的实现
interface IAggregate
{
IIterator GetIterator();
string this[int itemIndex]{set;get;}
int Count{get;}
}
编写具体聚合(集合)对象
现在我们已经准备好这两个接口,我们可以定义将保存对象集合的具体类。 让我们创建一个简单的类,它将保存 string
值的集合。我们将使用我们的迭代器来获取这些 string
值。
MyAggregate
的实现
class MyAggregate : IAggregate
{
List<string> values_ = null;
public MyAggregate()
{
values_ = new List<string>();
}
#region IAggregate Members
public IIterator GetIterator()
{
return new MyIterator(this);
}
#endregion
public string this[int itemIndex]
{
get
{
if (itemIndex < values_.Count)
{
return values_[itemIndex];
}
else
{
return string.Empty;
}
}
set
{
values_.Add(value);
}
}
public int Count
{
get
{
return values_.Count;
}
}
}</string>
实现具体迭代器
现在让我们实现 MyIterator
,它是 IIterator
的具体类。 通过具体集合类的值进行迭代的实际逻辑将在此类中。
MyIterator
的实现
class MyIterator : IIterator
{
IAggregate aggregate_ = null;
int currentIndex_ = 0;
public MyIterator(IAggregate aggregate)
{
aggregate_ = aggregate;
}
#region IIterator Members
public string FirstItem
{
get
{
currentIndex_ = 0;
return aggregate_[currentIndex_];
}
}
public string NextItem
{
get
{
currentIndex_ += 1;
if (IsDone == false)
{
return aggregate_[currentIndex_];
}
else
{
return string.Empty;
}
}
}
public string CurrentItem
{
get
{
return aggregate_[currentIndex_];
}
}
public bool IsDone
{
get
{
if (currentIndex_ < aggregate_.Count)
{
return false;
}
return true;
}
}
#endregion
}
所以现在,我们已经为迭代器模式的简单和初步实现准备好了所有构建块。 让我们看看我们如何使用迭代器来访问集合的值。
这是 Main
的实现
class Program
{
static void Main(string[] args)
{
MyAggregate aggr = new MyAggregate();
aggr[0] = "1";
aggr[1] = "2";
aggr[2] = "3";
aggr[3] = "4";
aggr[4] = "5";
aggr[5] = "6";
aggr[6] = "7";
aggr[7] = "8";
aggr[8] = "9";
aggr[9] = "10";
IIterator iter = aggr.GetIterator();
for (string s = iter.FirstItem; iter.IsDone == false; s = iter.NextItem )
{
Console.WriteLine(s);
}
}
}
现在我们有了一个迭代器模式的简单实现。 代码中可能存在许多可能的优化,但这里的目的是解释迭代器模式,因此代码以一种简单(也许不是那么高效)的方式编写。
关注点
.NET Framework 和 C# 语言在其代码中深度嵌入了迭代器模式。 IEnumerable
接口实际上是迭代器模式的促进者。 C# 中的泛型和集合类可以通过枚举器进行迭代,这实际上是一个迭代器模式的实现。
我们在本文中没有看到 C# 和 .NET 特定的迭代器模式实现。 使用语言和框架内置的迭代器肯定更有效且不易出错。 本文背后的想法是了解迭代器模式的工作原理,我们在 C# 中实现了一个简单的迭代器模式。
历史
- 2012 年 4 月 7 日:第一个版本