迭代器是一种对象,可以视为是一个集合的书签或者游标,表示一个集合中的某个位置,用以遍历集合中的部分或全部元素,它提供了一个方法顺序访问一个集合中的各个元素,而不暴露其内部表示,而C#已经为我们提供了几个迭代器的接口
枚举接口
枚举接口IEnumerable和IEnumerator是迭代器模式(iterator pattern)在C#中的实现。它们实现在集合上进行简单迭代的效果
IEnumerable接口
集合需要实现IEnumerable接口,才能被迭代,这个接口实现GetEnumerator方法,这个方法会返回一个IEnumerator对象,这就是我们的迭代器对象
IEnumerator接口
迭代器实现IEnumerator接口,这个接口有三个方法
- MoveNext 用于判断受否达到了迭代的终点
- Current 获取当前迭代的值
- Reset 重置迭代器
通过自定义类来实现枚举接口的功能
集合类
public class GameEngineEnumerable : IEnumerable
{
private string[] _gameEngines = new string[5] { "Unity", "Godot", "GameMakerStudio", "Cocos", "RPGMaker" };
public IEnumerator GetEnumerator()
{
return new GameEngineEnumerator(_gameEngines);
}
}
迭代器类
public class GameEngineEnumerator : IEnumerator
{
private string[] _gameEngines;
private int _position = -1; //索引
public GameEngineEnumerator(string[] gameEngines)
{
_gameEngines = new string[gameEngines.Length];
for (int i = 0; i < gameEngines.Length; i++)
{
_gameEngines[i] = gameEngines[i];
}
}
public bool MoveNext()
{
}
public void Reset()
{
}
public object Current { get; }
}
接下来实现 IEnumerator接口的三个方法
public bool MoveNext()
{
if (_position < _gameEngines.Length)
{
_position++;
return true;
}
return false;
}
public object Current
{
get
{
//判断越界情况
if (_position >= _gameEngines.Length)
{ return null;
}
return _gameEngines[_position];
}}
public void Reset()
{
_position = -1;
}
接下来,我们在主函数中实现遍历
public static void Main(string[] args)
{
GameEngineEnumerable gameEngine = new GameEngineEnumerable();
IEnumerator gameEngineEnumerator = gameEngine.GetEnumerator();
while (gameEngineEnumerator.MoveNext())
{
Console.WriteLine(gameEngineEnumerator.Current);
}
}
成功输出string数组里面的值
使用yield语句简化迭代
我们把包含 yield 语句的方法或属性称为迭代块 按照以上的方式实现迭代显然太过繁琐,C#为我们提供了yield语句简化了这个过程,我们可以使用yield语句提供下一个值(yield return)或者表示迭代结束(yield break)
现在,我们注释掉GameEngineEnumerator类,将GetEnumerator方法改为以下代码
public IEnumerator GetEnumerator()
{
//return new GameEngineEnumerator(_gameEngines);
for (int i = 0; i < _gameEngines.Length; i++)
{
yield return _gameEngines[i];
}
}
一样能实现相同的效果,但代码得到了极大的简化 而当我们执行yield break时,迭代器立即停止,MoveNext立即返回false,Current停留在最后一次保留的值上 在yield return执行完毕以后,函数并不会销毁,而是“休克”,等待返回值的IEnumerator执行下一次MoveNext(),yield return语句指定了枚举器中下一个可枚举项,迭代器在每次调用MoveNext函数时,会顺着上一次的枚举项(yield return)按照我们自己写的逻辑执行到下一个枚举项去
foreach
在C#中,我们可以使用foreach语句遍历可枚举集合,例如数组,哈希表,字典等,例如
foreach (var item in str)
{
Console.WriteLine(item);
}
而事实上,foreach内部就是由迭代器实现的,运行时并不直接支持 foreach 语句,C# 编译器会转换代码(foreach也类似于一种语法糖) 以上为官方的foreach写法,下面我们手动实现一个民间foreach
void ForeachFunc(string str)
{
IEnumerator e = str.GetEnumerator();
while(e.MoveNext())
{
Console.WriteLine(e.Current);
}
}
我们调用这个方法,会发现输出的结果是一样的,实际上,foreach的实现其实非常简单,就类似于上面的民间foreach,对集合调用GetEnumerator方法获取迭代器(string继承了IEnumerable接口),然后使用while循环,直到MoveNext方法返回false,而在循环的过程中,可以调用Current属性,获取迭代器当前所对应的值