浅谈yield

c#1.0使用foreach 语句可以轻松地迭代集合。在c#1.0中,创建枚举器仍需要做大量的工作。c#2.0添加了yield语句,以便于创建枚举器。下面我们浅谈下yield的使用:

1、包含yield语句的方法或属性称为迭代块迭代块必须声明为返回IEnumerator或IEnumerable接口。这个块可以包含多个yield return语句或yield break语句,但不能包含return语句

      yield return语句返回集合的一个元素,并移动到下一个元素上。

      yield break语句:停止迭代

yield 语句只能出现在 iterator 块中,该块可用作方法、运算符或访问器的体。这类方法、运算符或访问器的体受以下约束的控制:

不允许不安全块。

方法、运算符或访问器的参数不能是 ref 或 out。

yield 语句不能出现在匿名方法中。有关更多信息,请参见匿名方法(C# 编程指南)。

当和 expression 一起使用时,yield return 语句不能出现在 catch 块中或含有一个或多个 catch 子句的 try 块中。有关更多信息,请参见异常处理语句(C# 参考)。

 2、yield语句从本质上讲是运用了延迟计算(Lazy evaluation或delayed evaluation)的思想。在Wiki上可以找到延迟计算的解释:将计算延迟,直到需要这个计算的结果的时候才计算,这样就可以因为避免一些不必要的计算而改进性能,在合成一些表达式时候还可以避免一些不必要的条件,因为这个时候其他计算都已经完成了,所有的条件都已经明确了,有的根本不可达的条件可以不用管了。

      延迟计算来源自函数式编程,在函数式编程里,将函数作为参数来传递,你想呀,如果这个函数一传递就被计算了,那还搞什么搞,如果你使用了延迟计算,表达式在没有使用的时候是不会被计算的,比如有这样一个应用:x=expression,将这个表达式赋给x变量,但是如果x没有在别的地方使用的话这个表达式是不会被计算的,在这之前x里装的是这个表达式。举个例子,linq就是运用了延迟计算的思想。看下面的代码:

var result = from book in books
    
where
 book.Title.StartWiths(“t”)
    select book
if(state > 0
)
{
    
foreach(var item in
 result)
    {
        
//….
   
}
}

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

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

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

 

讲了yield的一些基础,觉得有必要讲下IEnumeratorIEnumerable接口区别:

 

public interface IEnumerable
{
    IEnumerator GetEnumerator();
}
 
public interface IEnumerator
{
    bool MoveNext();
    void Reset();
 
    Object Current { get; }
}


 1、一个Collection要支持foreach方式的遍历,必须实现IEnumerable接口(亦即,必须以某种方式返回IEnumerator object)。
 
2
IEnumerator object具体实现了iterator(通过MoveNext()Reset()Current)。
 
3
、从这两个接口的用词选择上,也可以看出其不同:IEnumerable是一个声明式的接口,声明实现该接口的class可枚举(enumerable的,但并没有说明如何实现枚举器(iterator);IEnumerator是一个实现式的接口IEnumerator object就是一个iterator
 
4
IEnumerableIEnumerator通过IEnumerableGetEnumerator()方法建立了连接,client可以通过IEnumerableGetEnumerator()得到IEnumerator object,在这个意义上,将GetEnumerator()看作IEnumerator objectfactory method也未尝不可。


IEnumerator 是所有枚举数的基接口。  
   
 
枚举数只允许读取集合中的数据。枚举数无法用于修改基础集合。 这也是为什么说“不要在foreach循环中修改元素的原因
“.
   
 
最初,枚举数被定位于集合中第一个元素的前面。Reset 也将枚举数返回到此位置。在此位置,调用   Current 会引发异常。因此,在读取 Current 的值之前,必须调用 MoveNext 将枚举数提前到集合的第一个元素。
  
   
 
在调用   MoveNext      Reset   之前,Current   返回同一对象。MoveNext      Current   设置为下一个元素。  

   
 
在传递到集合的末尾之后,枚举数放在集合中最后一个元素后面,且调用   MoveNext   会返回   false。如果最后一次调用   MoveNext   返回   false,则调用   Current   会引发异常。若要再次将   Current   设置为集合的第一个元素,可以调用   Reset,然后再调用   MoveNext  
   
 
只要集合保持不变,枚举数就将保持有效。如果对集合进行了更改(例如添加、修改或删除元素),则该枚举数将失效且不可恢复,并且下一次对   MoveNext      Reset   的调用将引发   InvalidOperationException如果在   MoveNext      Current   之间修改集合,那么即使枚举数已经无效,Current   也将返回它所设置成的元素。
  
   
 
枚举数没有对集合的独占访问权;因此,枚举一个集合在本质上不是一个线程安全的过程。甚至在对集合进行同步处理时,其他线程仍可以修改该集合,这会导致枚举数引发异常。若要在枚举过程中保证线程安全,可以在整个枚举过程中锁定集合,或者捕捉由于其他线程进行的更改而引发的异常。  

 

 

代码下载:/Files/qlb5626267/YieldDemo.rar

参考文献:你必须知道的.net

转载于:https://www.cnblogs.com/qlb5626267/archive/2009/05/08/1452517.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/423443.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NIO核心框架介绍

NIO共引入了4个概念: - 缓存区:表示数据存放的容器,提供可读写的数据缓存区; - 字符集:用来对缓存数据进行解码和编码,在字节和Unicode字符之间转换; - 通道:用来接收或发送数据&…

前端学习(16):跳转链接小练习

点击图片实现跳转 目录结构 header.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv&q…

云开发的数据库权限机制解读丨云开发101

在使用云开发进行开发时&#xff0c;数据库权限是一个让不少人困扰的部分&#xff0c;四种数据库权限&#xff0c;到底是什么意思&#xff1f;其各自的权限、应用场景都是什么&#xff1f;大多数人对于这个机制&#xff0c;还是模糊的。为了帮助大家进行更好的开发&#xff0c;…

jremind V0.1.3.0添加透明

这次就把弹出消息的窗口改成了透明&#xff0c;本来说是做鼠标穿透的&#xff0c;给做成了透明&#xff0c;先用着&#xff01;有时间了再改了。 转载于:https://www.cnblogs.com/joypen/archive/2009/05/11/1693125.html

Java中的NIO非阻塞编程

在JDK1.4以前&#xff0c;Java的IO操作集中在java.io这个包中&#xff0c;是基于流的阻塞API。对于大多数应用来说&#xff0c;这样的API使用很方便&#xff0c;然而&#xff0c;一些对性能要求较高的应用&#xff0c;尤其是服务端应用&#xff0c;往往需要一个更为有效的方式来…

Nginx----基础

静态资源服务 通过本地文件系统提供服务&#xff1a;对css&#xff0c;js文件&#xff0c;图片等静态文件 反向代理服务 缓存&#xff1a;将一些数据经常不变的&#xff0c;缓存到Nginx中&#xff0c;直接给用户提供服务 负载均衡 api服务 OpenResty 数据库的服务比较简单&…

c#开发IE控件

c#开发IE控件主要是对BHO对象是使用,但是我们知道BHO是一个COM对象,而在.NET下开发基于COM的应用,总觉得不是很简单,这里有受控代码与COM的调用,我查找了下,国内并没有此类信息,下面是译稿,翻译的不好,欢迎指出.介绍:我们在浏览Internet信息的时候,往往需要增强用户浏览信息的,…

JVM直接内存

概述 直接内存并不是虚拟机运行时数据区的一部分&#xff0c;也不是Java 虚拟机规范中农定义的内存区域。在JDK1.4 中新加入了NIO(New Input/Output)类&#xff0c;引入了一种基于通道(Channel)与缓冲区&#xff08;Buffer&#xff09;的I/O 方式&#xff0c;它可以使用native…

英语----专业单词

directive (滴 ‘ruai k tive) n&#xff1a;指令determine 【di tε:min】 (de ter min) v&#xff1a;确定 词根&#xff1a;term 边界 distributions (dis tree biu tion)v&#xff1a;分配 C&#xff1a;发行版 exact (衣 ge za k t) adj&#xff1a;确切的 ex&#xff1a…

js遍历table

js遍历table var tableObj document.getElementById("tableName");var str "";for(var i0;i<tableObj.rows.length;i){for(varj0;j<tableObj.rows[i].cells.length;j){//str tableObj.rows[i].cells[j].innerHTML" ";for(var z0;z<…

深入理解JVM—JVM内存模型

我们知道&#xff0c;计算机CPU和内存的交互是最频繁的&#xff0c;内存是我们的高速缓存区&#xff0c;用户磁盘和CPU的交互&#xff0c;而CPU运转速度越来越快&#xff0c;磁盘远远跟不上CPU的读写速度&#xff0c;才设计了内存&#xff0c;用户缓冲用户IO等待导致CPU的等待成…

Nginx----进阶

用Nginx搭建一个静态的web资源服务器/动静分离 简单使用 1、可以在安装的nginx目录新建自己的目录zy&#xff08;和conf在一个目录下&#xff0c;也就是和html目录在一个目录下&#xff0c;注意如果使用/zy&#xff0c;那么zy目录需要创建在linux根目录&#xff09;&#xff0c…

ASP.NET页面生命周期描述

ASP.NET页面生命周期描述2008-09-12 09:25在以前写个一篇关于ASP.NET页面生命周期的草稿,最近又看了看ASP.NET&#xff0c;做个补充,看看页面初始过程到底是怎么样的 下面是ASP.NET页面初始的过程: 1. Page_Init(); 2. Load ViewState; 3. Load Postback data; 4. Page_Load();…

小程序·云开发的HTTP API调用丨实战

小程序云开发之httpApi调用。 小程序云开发之httpApi调用&#xff08;返回“47001处理”&#xff09; 技术栈 采用 nodejs express 搭建web服务器&#xff0c;采用 axios 请求第三方 httpApi nodejsexpressaxios 项目结构 通过应用生成器工具 express-generator 可以快速创建一…

Java hashCode() 和 equals()使用的场景

第3部分 hashCode() 的作用 hashCode() 的作用是获取哈希码&#xff0c;也称为散列码&#xff1b;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。 hashCode() 定义在JDK的Object.java中&#xff0c;这就意味着Java中的任何类都包含有hashCo…

坚守基本的职业信仰

深陷公司的政治斗争会让任何当事人都疲于奔命&#xff0c;尤其是当这种斗争的双方都是您的上级。除非您一开始就能够知道最终的斗争结果&#xff0c;否则任何具有倾向性的言行都会给自己带来难堪。 深陷入别人的职业斗争是一个职业经理人职业生涯最大的不幸&#xff0c;除非…

Zookeeper----基本原理

Zookeeper作用是什么&#xff1f; 协调分布式系统中的多个服务器&#xff0c;使得系统可以正常工作。 Zookeeper提供了什么&#xff1f; 实际上他只提供了三个东西&#xff0c;一个是文件系统&#xff0c;一个是通知机制&#xff0c;还有一个是集群管理机制 Zookeeper可以做什么…

Java 中 Comparable 和 Comparator 比较

Comparable 简介 Comparable 是排序接口。 若一个类实现了Comparable接口&#xff0c;就意味着“该类支持排序”。 即然实现Comparable接口的类支持排序&#xff0c;假设现在存在“实现Comparable接口的类的对象的List列表(或数组)”&#xff0c;则该List列表(或数组)可以通…

spgridview的过滤功能回调时发生错误~

代码中启用了过滤功能&#xff0c; 但当点击过滤的列时发生错误&#xff1a; error: spgridview_filtercallbackerrorhandler() was called - result 回调时发生错误... 详细见&#xff1a; HTML代码&#xff1a; AllowFiltering"True"FilterDataFields",Name,,…

web---SSL/TSL

对称加密 加密算法秘钥&#xff08;双方持有相同的秘钥&#xff09;&#xff0c;A使用加密算法秘钥加密文件&#xff0c;B使用加密算法秘钥解密文件&#xff1b; 演示一个对称加密RC4算法 注意&#xff1a;RC4加密是有漏洞的 明文和秘钥进行异或得到密文&#xff0c;密文和秘钥…