【笔记】【LINQ编程技术内幕】第四章 yield return: 使用.NET的状态机生成器

理解yield return 的工作方式

yield return 是一个编译器暗示,告诉编译器要生成一个状态机。每当遇到一个含有yield return的代码时,则返回yield return后的表达式的值,并保存当前再代码中的位置。下次调用迭代器函数时,键从该位置重新开始执行。

static void Main(string[] args)
{
	foreach (int i in GetEvents())
	{
		Debug.WriteLine("Main : " + i);
	}
}

public static IEnumerable<int> GetEvents()
{
	Debug.WriteLine("Init : ");
	var integers = new[] { 1, 2, 3, 4};
	
	foreach (int i in integers)
	{
		Debug.WriteLine("Loop Before : " + i);
		if (i % 2 == 0)
		{
			Debug.WriteLine("Pause : " + i);
			// 返回当前值,并保存当前状态,下一次调用从此开始
			yield return i;
		}
		Debug.WriteLine("Loop After : " + i);
	}
	Debug.WriteLine("IDone : ");
}

输出结果:
Init :
Loop Before : 1
Loop After : 1
Loop Before : 2
Pause : 2
Main : 2
Loop After : 2
Loop Before : 3
Loop After : 3
Loop Before : 4
Pause : 4
Main : 4
Loop After : 4
IDone :

由输出结果得到以下执行流程:

  1. 执行数组初始化 (Init)
  2. 在Foreach循环中取出第一个元素(Loop Before : 1)
  3. 不满足偶数条件(Loop After : 1)
  4. 在Foreach循环中取出第二个元素(Loop Before : 2)
  5. 满足偶数条件(Pause : 2)
  6. 执行yield return 返回 2,并暂停于此
  7. 主函数输出值(Main : 2)
  8. 主函数执行下一次循环,从上次暂停的位置开始执行(Loop After : 2)
  9. 主函数循环结束 (Main : 4)
  10. 取消最后一次的暂定(Loop After : 4)
  11. 被调函数执行完成(IDone )

yield return 遵循的规则

由yield return 关键字词组生成的枚举器还会实现IDisposable。此外,在使用这个可枚举对象的地方,还存在一个隐含的try finally块

  • yield return 后面要跟一个表达式,该表达式必须能被转换为迭代器所使用的类型
  • yield return 只能位于迭代器块中;这个迭代器块既可以放在方法体中,也可以放在运算符函数中,还可以用在访问器中
  • yield return 不能放在不安全的块中
  • 方法、运算符或访问器的参数不能是ref 或 out
  • yield return 语句不能出现在catch块中,也不能出现在带有一个或多个catch子句的try块中
  • yield 语句不能出现在匿名方法中

yield return

含有 yield return 语句的函数可以用来做很多事情,比如返回IEnumberable。这就意味着,你可以在任何类型的对象上获得可枚举功能,而且无需编写类型化集合类以及相关的处理代码。

class Program
{
	static void Main(string[] args)
	{
		BinaryTree<int> integers = new BinaryTree<int>();
		integers.AddRange(8, 5, 6, 7, 3, 4, 13, 21, 1, 17);

		// display conceptual tree
		integers.Print();
		Console.ReadLine();
		Console.Clear();

		// write in order
		foreach (var item in integers.Inorder)
			Console.WriteLine(item);
	}
}

//                   8
//                  / \
//                 5   13
//                / \   \
//               3   6   21
//              / \   \  /
//             1   4  7 17

public class Node<T> where T : IComparable<T>
{
	public T data;

	public Node<T> LeftNode  {get;set;}
	public Node<T> RightNode {get;set;}

	/// <summary>
	/// Initializes a new instance of the Node class.
	/// </summary>
	/// <param name="data"></param>
	public Node(T data) => this.data = data;
	public override string ToString() => data.ToString();
}

/* 二叉树类,使用yield return 来遍历每个节点。
 * 为了能够比较泛型类BinaryTree<T>得数据值,所有类型T都必须是IComparable
 */
public class BinaryTree<T> where T : IComparable<T>
{
	private Node<T> root;
	public Node<T> Root
	{
		get { return root; }
		set { root = value; }
	}

	/// <summary>根节点存在得情况下,增加节点;根节点不存在,以当前数据创建根节点</summary>
	public void Add(T item)
	{
		if (root == null)
		{
			root = new Node<T>(item);
			return;
		}
		
		Add(item, root);
	}

	/// <summary>增加子节点</summary>
	private void Add(T item, Node<T> node)
	{
		if (item.CompareTo(node.data) < 0)
		{
			if (node.LeftNode == null)
				node.LeftNode = new Node<T>(item);
			else
				Add(item, node.LeftNode);
		}
		else if (item.CompareTo(node.data) > 0)
		{
			if (node.RightNode == null)
				node.RightNode = new Node<T>(item);
			else
				Add(item, node.RightNode);
		}

		// silently discard it
	}

	/// <summary>一次增加多个节点</summary>
	public void AddRange(params T[] items)
	{
		foreach (var item in items)
			Add(item);
	}

	/// <summary>
	/// error in displaying tree - 7 is overwritten 17! 
	/// </summary>
	public void Print()
	{
		Print(root, 0, Console.WindowWidth / 2);
	}

	public IEnumerable<T> Inorder => GetInOrder(this.root);

	IEnumerable<T> GetInOrder(Node<T> node)
	{
		if (node.LeftNode != null)
			foreach (T item in GetInOrder(node.LeftNode))
				yield return item;

		yield return node.data;

		if (node.RightNode != null)
			foreach (T item in GetInOrder(node.RightNode))
			{
				yield return item;
			}
	}


	private void Print(Node<T> item, int depth, int offset)
	{
		if (item == null)
			return;
			
		Console.CursorLeft = offset;
		Console.CursorTop = depth;
		Console.Write(item.data);

		if (item.LeftNode != null)
			Print("/", depth + 1, offset - 1);
		Print(item.LeftNode, depth + 2, offset - 3);

		if (item.RightNode != null)
			Print("\\", depth + 1, offset + 1);
		Print(item.RightNode, depth + 2, offset + 3);
	}

	private void Print(string s, int depth, int offset)
	{
		Console.CursorLeft = offset;
		Console.CursorTop = depth;
		Console.Write(s);
	}
}

yield break

如果因为某种原因,需要在最后得元素被返回之前就结束掉yield return,则可以使用yield break关键字词组。

int depth = 0;
IEnumerable<T> GetInOrder(Node<T> node)
{
	if(depth++ > 4) yield break;
	
	if (node.LeftNode != null)
		foreach (T item in GetInOrder(node.LeftNode))
			yield return item;

	yield return node.data;

	if (node.RightNode != null)
		foreach (T item in GetInOrder(node.RightNode))
		{
			yield return item;
		}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhy29563

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值