c# 迭代器

    最近重新看《深入理解C#》, 看到迭代器一章,算是笔记吧。

   迭代器模式是行为模式的一种范例,行为模式是一种简化对象之间通信的设计模。它允许你访问一个数据项序列中的所有元素,而无须关心序列是什么类型(数组,列表,链表,或任何其他类型)。

  在.Net中,迭代器模式是通过IEnumerator和IEunmerable接口及他们的泛型等价物来封装的。如果某个类型实现了IEnumerable接口,就意味着它可以被迭代访问。

Class要实现IEunmerable,IEunmerable要实现返回值为IEnumerator的GetEnumerator方法。代码如下:

 1  public class Iteration : IEnumerable
 2     {
 3         private readonly object[] _values;//要迭代的数组
 4         private readonly int _startingPoint;//迭代的起始索引
 5 
 6         public Iteration(object[] values, int startingPoint)
 7         {
 8             _values = values;
 9             _startingPoint = startingPoint;
10         }
11         public IEnumerator GetEnumerator()
12         {
13             for (int i = 0; i < _values.Length; i++)
14             {
15                 yield return _values[(i + _startingPoint)%_values.Length];
16             }
17            
18         }
19     }

其实上面的GetEnumerator()中的for循环可以用Linq自带的GetEnumerator()来实现更简单

 public IEnumerator GetEnumerator()
 {
    return _values.Select((t, i) => _values[(i + _startingPoit) % _values.Length]).GetEnumerator();
 }n
yield 是在c#2.0中加入的,主要是为了更加简便的实现迭代器,这句代码就是告诉c#编译器这不是一个普通方法,而是一个实现的迭代器块的方法。这个方法被声明为返回一个IEnumerator接口。yield其实就是一个语法糖,背后还是有一堆繁琐的操作来支持遍历,如果在c#1.0就需要自己去写这些了,上面 GetEnumerator 就相当于下面的c#1.0里的代码:
 1 class  Iterator:IEnumerator
 2     {
 3         private Iteration _parent;
 4         private int _position;
 5 
 6         public Iterator(Iteration parent)
 7         {
 8             _parent = parent;
 9             _position = -1;
10         }
11 
12         public bool MoveNext()
13         {
14             if (_position != _parent._values.Length)
15             {
16                 _position++;
17             }
18 
19             return _position < _parent._values.Length;
20         }
21 
22         public void Reset()
23         {
24             _position = -1;
25         }
26 
27         public object Current
28         {
29             get
30             {
31                 if (_position == -1 || _position == _parent._values.Length)
32                 {
33                     throw new InvalidOperationException();
34                 }
35                 int index = _position + _parent._startingPoint;
36                 index = index%_parent._values.Length;
37                 return _parent._values[index];
38 
39             } 
40         }
41     }

   可以看到这么简单的一个任务使用好多代码,而且这只是一个简单的例子,没有太多的状态需要跟踪,也没有尝试检查集合是否在两次迭代之中改变。实现一个简单的迭代器都需要花费这么大精力,所以很少有人在C#1中实现这个模式。而在C#2中yield的出现大大简化了迭代器的实现,所有的一切都交给编译器去做了。

 下面来看一看迭代器的执行流程:

 1  private static IEnumerable<int> CreateEnumerable()
 2         {
 3             Console.WriteLine("{0} 迭代开始", _padding);
 4 
 5             for (int i = 0; i < 3; i++)
 6             {
 7                 Console.WriteLine("{0} 第 {1} 次迭代", _padding, i);
 8                 yield return i;
 9                 Console.WriteLine("{0}下一次迭代", _padding);
10             }
11 
12             Console.WriteLine("{0} 迭代最后一个值", _padding);
13             yield return -1;
14             Console.WriteLine("{0} 迭代结束", _padding);
15         }
 1 private static string _padding = new string(' ', 30);
 2 
 3  IEnumerable<int> iterable = CreateEnumerable();
 4             IEnumerator<int> iterator = iterable.GetEnumerator();
 5             Console.WriteLine("开始");
 6 
 7             while (true)
 8             {
 9                 Console.WriteLine("开始调用 MoveNext()....");
10                 bool result = iterator.MoveNext();
11                 Console.WriteLine("....MoveNext result={0}", result);
12                 if (!result)
13                 {
14                     break;
15                 }
16 
17                 Console.WriteLine("当前值");
18                 Console.WriteLine("....Current result={0}", iterator.Current);
19 
20             }

下面是输出结果:

 可以看到:

  在第一次调用MoveNext 之前, CreateEnumerable中的代码不会被调用.

  所有的工作都在调用MoveNext的时候就完成了,获取Current的值不会执行任何代码.

  在yield return的位置,代码就停止执行,在下一次调用MoveNext的时候又继续执行.

  在同一个方法中的不同地方可以书写多个yield return 语句.

  代码不会在最后的yield return处结束,相反,而是通过返回false的MoveNext调用来结束方法的执行.

如果想提前终止迭代怎么办呢,可以用yield break语句来实现,它可以让当前对MoveNext的调用返回false,实际上终止了迭代器的运行.

那么为什么要用迭代器呢,好多取数据其实直接用for循环就可以实现,iterator的优势是什么呢?

好处就是降低了数据结构的耦合性,将遍历一个序列的操作与此序列底层结构相分离.所以使用迭代器是面向对象封装性的体现。其实LINQ中定义的查询方法,如Select,Where等返回就是一个实现了IEnumerable接口的类型,而我们不用去关心具体的类型是什么,直接用foreach迭代就可以了.

 

转载于:https://www.cnblogs.com/zoezhangs/archive/2013/02/21/2919783.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值