基本概念
迭代器(Iterator)是一种设计模式,允许你遍历集合中的元素而不暴露其底层结构。在C#中,迭代器是通过实现 IEnumerable
和 IEnumerator
接口或者使用 yield
关键字来实现的。
用途
遍历集合:迭代器用于遍历集合(如数组、列表等)中的元素。
自定义集合遍历:可以自定义集合的遍历方式,而不影响集合的实现。
在游戏开发中的用途
遍历游戏对象:遍历游戏中的所有对象,如敌人、道具、玩家等。
路径查找:遍历路径点来实现路径查找算法。
动画帧处理:遍历和处理动画帧。
枚举器的工作原理
初始状态:枚举器的位置在第一个元素之前,位置为 -1。
调用
MoveNext
:将枚举器移动到集合的下一个元素。如果枚举器在初始状态,则移动到第一个元素。访问
Current
:返回当前元素的值。继续调用
MoveNext
:直到到达集合的末尾,此时MoveNext
返回false
。
为什么从 -1 开始?
初始化:从 -1 开始可以确保在第一次调用
MoveNext
之前,枚举器不会指向集合中的任何元素。防止直接访问:避免在未调用
MoveNext
之前直接访问Current
属性,因为此时Current
的值未定义。一致性:与其他编程语言中的迭代器行为保持一致,使开发者更容易理解和使用。
示例代码及解释
1.实现一个自定义枚举器
using System; using System.Collections; using System.Collections.Generic; public class CustomCollection : IEnumerable<int> { private int[] items = { 1, 2, 3, 4, 5 }; public IEnumerator<int> GetEnumerator() { return new CustomEnumerator(items); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private class CustomEnumerator : IEnumerator<int> { private int[] _items; private int _position = -1; public CustomEnumerator(int[] items) { _items = items; } public bool MoveNext() { _position++; return (_position < _items.Length); } public void Reset() { _position = -1; } public int Current { get { if (_position < 0 || _position >= _items.Length) throw new InvalidOperationException(); return _items[_position]; } } object IEnumerator.Current => Current; public void Dispose() { // 清理资源 } } } class Program { static void Main() { CustomCollection collection = new CustomCollection(); foreach (var item in collection) { Console.WriteLine(item); } } }
解释:
CustomCollection
实现了IEnumerable<int>
接口。CustomEnumerator
实现了IEnumerator<int>
接口。_position
初始值为 -1,表示在第一个元素之前的位置。MoveNext
方法将_position
增加 1,并返回是否在集合范围内。Current
属性返回当前位置的元素,如果当前位置不合法则抛出异常。
2.使用 yield return
语句:
public IEnumerable<int> GetPowersOfTwo(int n)
{
int result = 1;
for (int i = 0; i < n; i++)
{
yield return result;
result *= 2;
}
}
示例代码
public class GameLevel
{
private List<string> enemies = new List<string> { "Orc", "Troll", "Goblin" };
public IEnumerator<string> GetEnumerator()
{
foreach (string enemy in enemies)
{
yield return enemy;
}
}
}
public class Program
{
public static void Main()
{
GameLevel level = new GameLevel();
foreach (string enemy in level)
{
Console.WriteLine(enemy);
}
}
}
优点和缺点
优点
一致性:与其他语言和框架的迭代器行为一致,便于理解和使用。
安全性:确保在调用
MoveNext
之前不能访问Current
,避免访问未定义的值。初始化简单:从 -1 开始,使得第一次调用
MoveNext
就能正确定位到第一个元素。
缺点
理解成本:对于初学者来说,理解为什么从 -1 开始可能有点困惑,需要额外的解释。
基础练习题
创建一个简单的自定义集合类,包含整数数组,实现
IEnumerable<int>
接口,并使用自定义枚举器遍历集合中的元素。修改上述代码,使枚举器能够遍历一个字符串数组,打印出所有字符串。
进阶练习题
实现一个自定义链表类,并实现
IEnumerable<T>
接口,使用自定义枚举器遍历链表中的元素。实现一个栈(Stack)类,并实现
IEnumerable<T>
接口,使用自定义枚举器遍历栈中的元素(从栈顶到栈底)。