什么时候可以使用yield的关键字来定义迭代器?
- 迭代器的返回类型必须是IEnumerable、IEnumerable<T>、IEnumerator 或 IEnumerator<T>
- 迭代器的入参不能包括ref或out类型的参数
首先,我们定义一个简单的遍历。
static void Main(string[] args)
{
List<Person> persons = new List<Person>();
persons.Add(new Person { SaySome = "Hello World,I am Sheldon" });
persons.Add(new Person { SaySome = "Hello World,I am Penny" });
persons.Add(new Person { SaySome = "Hello World,I am Amy" });
foreach (var item in persons)
{
Console.WriteLine(item.SaySome);
}
}
它的输出结果:
接下来,我们使用yield,来实现同样的效果。
static void Main(string[] args)
{
foreach (var item in GetEnumerator())
{
Console.WriteLine(item.SaySome);
}
} public static IEnumerable<Person> GetEnumerator()
{
yield return new Person { SaySome = "Hello World,I am Sheldon" };
yield return new Person { SaySome = "Hello World,I am Penny" };
yield return new Person { SaySome = "Hello World,I am Amy" };
}
虽然,我们得到了同样的结果,但是yield到底是如何做到的?
(又是翻书,又是看msdn,终于得到了答案)
使用yield语句时,它会自动生成一个枚举器,而不是仅仅生成一个包含项的列表。
—————————————————— 以上内容关于yield描述了大概 ———————————————————————
——————————————————以下内容主要说明foreach如何迭代———————————————————————
这个枚举器通过foreach调用,foreach中依次访问每一项时,就会访问这个枚举器,从而达到迭代大量数据,而无须一次把所有的数据写到内存中。
关于枚举器,我查看了system.collection.generic空间下的源码。
为了知道foreach中是如何通过枚举器来工作的。
我们来根据上面的List集合声明一个简单的枚举器
(这个枚举器,只是为了简单的说明一下问题)
public class GameMoves
{
private IEnumerator cross;
private IEnumerator circle;
public GameMoves()
{
cross = Cross();
circle = Circle();
}
private int move = ;
const int MaxMoves = ;
public IEnumerator Cross()
{
while (true)
{
Console.WriteLine("Cross, move {0}", move);
if (++move >= MaxMoves)
{
yield break;
}
yield return circle;
}
}
public IEnumerator Circle()
{
while (true)
{
Console.WriteLine("Circle,move{0}",move);
if (++move>=MaxMoves)
{
yield break;
}
yield return cross;
}
}
}
重写一下Main方法
var game = new GameMoves();
//将枚举器设置为由game.Cross()返回的枚举器类型
IEnumerator enumerator = game.Cross();
//第一次调用 MoveNext()时,会调用Cross()方法,Cross()方法使用yield返回另一个枚举器
while (enumerator.MoveNext())
{
//返回的值可以用Current属性访问,并设置为enumerator变量,用于下一次循环
enumerator = enumerator.Current as IEnumerator;
}
通过上面的例子我们能看出使用while来变向说明foreach的内部执行方式。
通过 foreach 语句或 LINQ 查询来使用迭代器方法。
foreach 循环的每次迭代都会调用迭代器方法。 迭代器方法运行到 yield return 语句时,会返回一个 expression,并保留当前在代码中的位置。 下次调用迭代器函数时,将从该位置重新开始执行。
(部分源自msdn)