C#特性 迭代器(下) yield以及流的延迟计算

2019-12-26 11:17:15王冬梅
  1. var result = from book in books    where book.Title.StartWiths(“t”) 
  2.   select book  if(state > 0) 
  3. {    foreach(var item in result) 
  4.   {      //…. 
  5. }  } 
?

result是一个实现了IEnumerable<T>接口的类(在Linq里,所有实现了IEnumerable<T>接口的类都被称作sequence),对它的foreach或者while的访问必须通过它对应的IEnumerator<T>的MoveNext()方法,如果我们把一些耗时的或者需要延迟的操作放在MoveNext()里面,那么只有等到MoveNext()被访问,也就是result被使用的时候那些操作才会执行,而给result赋值啊,传递啊,什么的,那些耗时的操作都没有被执行。

如果上面这段代码,最后由于state小于0,而对result没有任何需求了,在Linq里返回的结果都是IEnumerable<T>的,如果这里没有使用延迟计算,那那个Linq表达式不就白运算了么?如果是Linq to Objects还稍微好点,如果是Linq to SQL,而且那个数据库表又很大,真是得不偿失啊,所以微软想到了这点,这里使用了延迟计算,只有等到程序别的地方使用了result才会计算这里的Linq表达式的值的,这样Linq的性能也比以前提高了不少,而且Linq to SQL最后还是要生成SQL语句的,对于SQL语句的生成来说,如果将生成延迟,那么一些条件就先确定好了,生成SQL语句的时候就可以更精练了。还有,由于MoveNext()是一步步执行的,循环一次执行一次,所以如果有这种情况:我们遍历一次判断一下,不满足我们的条件了我们就退出,如果有一万个元素需要遍历,当遍历到第二个的时候就不满足条件了,这个时候我们就可就此退出,后面那么多元素实际上都没处理呢,那些元素也没有被加载到内存中来。

延迟计算还有很多惟妙惟肖的特质,也许以后你也可以按照这种方式来编程了呢。写到这里我突然想到了Command模式,Command模式将方法封装成类,Command对象在传递等时候是不会执行任何东西的,只有调用它内部那个方法他才会执行,这样我们就可以把命令到处发,还可以压栈啊等等而不担心在传递过程中Command被处理了,也许这也算是一种延迟计算吧。

本文也只是很浅的谈了一下延迟计算的东西,从这里还可以牵扯到并发编程模型和协同程序等更多内容,由于本人才疏学浅,所以只能介绍到这个地步了,上面一些说法也是我个人理解,肯定有很多不妥地方,欢迎大家拍砖。