yield实现迭代以及APM

  yield关键字用于迭代器块中用于枚举数提供值或发出一个迭代结束标识,它只能出现在iterator块中。

先看看一个传统迭代器的编写:

Node结点类
1 public class Node
2 {
3 public object data;
4 public Node next;
5
6 public override string ToString()
7 {
8 return data.ToString();
9 }
10 }

自定义一个集合并可枚举:

Node集合类
public class NodeCollection : IEnumerable<Node>
{

private Node[] nodes;
private int index;
private int capacity;

public NodeCollection(int capacity)
{
nodes
= new Node[capacity];
index
= 0;
this.capacity = capacity;
}

public Node this[int _index]
{
get
{
return nodes[_index];
}

}

public void Add(Node node)
{
if (index > capacity - 1)
{
throw new ArgumentException("run out of capacity");
}
nodes[index
++] = node;
}

#region IEnumerable<Node> Members

public IEnumerator<Node> GetEnumerator()
{
return new NodeEnumerator(nodes);
}

#endregion

}
该集合是的迭代器是用传统方法来实现的,需要实现:IEnumerator<>

Node集合枚举器
1 public class NodeEnumerator : IEnumerator<Node>
2 {
3 public Node[] nodes;
4 int index;
5
6 public NodeEnumerator(Node[] _nodes)
7 {
8 this.nodes = new Node[_nodes.Length];
9 _nodes.CopyTo(nodes, 0);
10 index = -1;
11 }
12
13 #region IEnumerator<Node> Members
14
15 public Node Current
16 {
17 get
18 {
19 return nodes[index];
20 }
21 }
22
23 #endregion
24
25 #region IDisposable Members
26
27 public void Dispose()
28 {
29 nodes = null;
30 }
31
32 #endregion
33
34 #region IEnumerator Members
35
36 object System.Collections.IEnumerator.Current
37 {
38 get { return nodes[index]; }
39 }
40
41 public bool MoveNext()
42 {
43 index++;
44 if (index < nodes.Length)
45 {
46 return true;
47 }
48 else
49 {
50 return false;
51 }
52 }
53
54 public void Reset()
55 {
56 index = -1;
57 }
58
59 #endregion
60 }

看得出来很繁锁,这在.net 1.1框架的做法,1.1之后不需要这么做,直接用yield关键字搞定一切,看代码:

yield的枚举器
1 public IEnumerator<Node> GetEnumerator()
2 {
3 //return new NodeEnumerator(nodes);
4   for (int i = 0; i < capacity; i++)
5 {
6 yield return nodes[i];
7 }
8 }
非常简单,这完全符合MS的原则:Write less code,Do more things

再看看得用这yield关键字改变传统的APM编程模型,通常利用传统异步编程模型的都是一个BeginXXX(AsyncCallback)的时候,然后EndXXX(IAsyncResult),程序写起来流畅,因为老是要写callback函数,要做的的事其实要让BeginXXX完成后,直接yield return number 返回一个得用枚举器中断当前线程,当BeginXXX()操作完成后,只需要在它的回调函数完成一次MoveNext()操作再执行yield return number之后的代码,这时候其实现就调用 EndXXX来完成本次异步调用,看代码

下载
1 static void Main()
2 {
3 DownloadContext context = new DownloadContext();
4 AsyncDownload download = new AsyncDownload();
5 context.Start(download.Download(context));
6
7 Console.Read();
8 }
9
10 public IEnumerator<int> Download(DownloadContext context)
11 {
12 var request = HttpWebRequest.Create("http://www.baidu.com");
13 var ar = request.BeginGetResponse(context.Continue(), null);
14 yield return 1;
15 var response = request.EndGetResponse(ar);
16
17 byte[] buffer = new byte[1024];
18 using (Stream sr = response.GetResponseStream())
19 using(StreamReader sr = new StreamReader(response.GetResponseStream()))
20 {
21 //Console.WriteLine(sr.ReadToEnd());
22 var ar2 = sr.BeginRead(buffer, 0, buffer.Length, context.Continue(), null);
23 //Console.WriteLine(BitConverter.ToString(buffer));
24 yield return 1;
25 sr.EndRead(ar2);
26 }
27 }
28 }
29
30 public class DownloadContext
31 {
32 private IEnumerator<int> enumerator;
33
34 public AsyncCallback Continue()
35 {
36 return (ar) => enumerator.MoveNext();
37 }
38
39 public void Start(IEnumerator<int> enumerator)
40 {
41 this.enumerator = enumerator;
42 enumerator.MoveNext();
43 }
44 }

这里有更详细的介绍关于APM编程的:http://www.cnblogs.com/yuyijq/archive/2011/02/24/1963326.html

我借鉴了作者的思路 ,自己再理一下,其实都是些老掉牙的技术了,手痒的时候再写一遍未尝不是一件好事!

原文地址:https://www.cnblogs.com/repository/p/2026332.html