迭代器模式是设计模式中行为模式(behavioral pattern)的一个例子,他是一种简化对象间通讯的模式,也是一种非常容易理解和使用的模式。简单来说,迭代器模式使得你能够获取到序列中的所有元素而不用关心是其类型是array,list,linked list或者是其他什么序列结构。这一点使得能够非常高效的构建数据处理通道(data pipeline)–即数据能够进入处理通道,进行一系列的变换,或者过滤,然后得到结果。事实上,这正是LINQ的核心模式。
在.NET中,迭代器模式被IEnumerator和IEnumerable及其对应的泛型接口所封装。如果一个类实现了IEnumerable接口,那么就能够被迭代;调用GetEnumerator方法将返回IEnumerator接口的实现,它就是迭代器本身。迭代器类似数据库中的游标,他是数据序列中的一个位置记录。迭代器只能向前移动,同一数据序列中可以有多个迭代器同时对数据进行操作。
在C#1中已经内建了对迭代器的支持,那就是foreach语句。使得能够进行比for循环语句更直接和简单的对集合的迭代,编译器会将foreach编译来调用GetEnumerator和MoveNext方法以及Current属性,如果对象实现了IDisposable接口,在迭代完成之后会释放迭代器。但是在C#1中,实现一个迭代器是相对来说有点繁琐的操作。C#2使得这一工作变得大为简单,节省了实现迭代器的不少工作。
yield可以大大的简化迭代器代码,让Coder写起来更加轻松自在,我们的迭代代码可以这样写:
class Program
{
static void Main(string[] args)
{
Iterator1 iterator = new Iterator1();
foreach (var item in iterator)
{
Console.WriteLine(item);
}
}
}
public class Iterator1 : IEnumerable
{
public List<int> list = new List<int>() { 1, 2, 3, 4, 5 };
public IEnumerator GetEnumerator()
{
for (int i = 0; i < list.Count; i++)
{
yield return list[i];
}
}
}
有些人看到这可能还是迷惑,因为大部分的程序员的思路都是线性的,上面的Iteration类的GetEnumerator函数的for循环不是一下都遍历完了吗,怎么还能给foreach用,好蒙啊。。。yield很神奇吧?是这样的:Jon Skeet说:“迭代器模式的一个重要方面就是:不用一次返回所有数据,调用代码一次只需获取一个元素。”你可以理解为每次执行yield return都能够返回一个数据并暂停当前的状态,那暂停的状态什么时候会继续呢?在下一次调用到MoveNext的时候。什么时候会调用MoveNext?foreach执行完一次,进入下一次的时候。
如果还不是很明白,我们再来看看《c# in Depth》的经典例子:
class IteratorWorkflow
{
static readonly string Padding = new string(’ ', 30);
static IEnumerable<int> GetEnumerable()
{
Console.WriteLine("{0}Start of GetEnumerator()", Padding);
for (int i = 0; i < 3; i++)
{
Console.WriteLine("{0}About to yield {1}", Padding, i);
yield return i;
Console.WriteLine("{0}After yield", Padding);
}
Console.WriteLine("{0}Yielding final value", Padding);
yield return -1;
Console.WriteLine("{0}End of GetEnumerator()", Padding);
}
public static void Main()
{
IEnumerable<int> iterable = GetEnumerable();
IEnumerator<int> iterator = iterable.GetEnumerator();
Console.WriteLine("Starting to iterate");
while (true)
{
Console.WriteLine("Calling MoveNext()...");
bool result = iterator.MoveNext();
Console.WriteLine("... MoveNext result={0}", result);
if (!result)
{
break;
}
Console.WriteLine("Fetching Current...");
Console.WriteLine("... Current result={0}", iterator.Current);
}
}
}
我相信,如果你有对照着这个例子认真分析一遍的话,应该就能掌握yield这个知识点了,如果还不清楚,代码Copy下来,自己跑一遍~~
最后有几个知识点总结归纳一下:
1·在遇到yield break或者返回IEnumerator的函数体结束前,不管yield return 的值为多少,MoveNext都是会返回True。
2·在第一次调用MoveNext之前,返回IEnumerable的代码都不会执行,即使你有主动去调用它。
3·执行到yield return的地方,代码就暂停了,并返回相应的值,在下一次调用MoveNext时,从上次暂停的地方继续执行。
4·yield return 代码不能放入try…catch块中,但是能放入try…finally块中。
作者:BlueBones_fan
来源:CSDN
原文:https://blog.csdn.net/fdyshlk/article/details/80215192
版权声明:本文为博主原创文章,转载请附上博文链接!