[转]C#中yield用法
yield 关键字向编译器指示它所在的方法是迭代器块。编译器生成一个类来实现迭代器块中表示的行为。在迭代器块中,yield 关键字与 return 关键字结合使用,向枚举器对象提供值。这是一个返回值,例如,在 foreach 语句的每一次循环中返回的值。yield 关键字也可与 break 结合使用,表示迭代结束。
例子:
yield return <expression>;
yield break;
在 yield return 语句中,将计算 expression 并将结果以值的形式返回给枚举器对象;expression 必须可以隐式转换为 yield 类型的迭代器。
在 yield break 语句中,控制权将无条件地返回给迭代器的调用方,该调用方为枚举器对象的 IEnumerator.MoveNext 方法(或其对应的泛型System.Collections.Generic.IEnumerable<T>)或 Dispose 方法。
yield 语句只能出现在 iterator 块中,这种块可作为方法、运算符或访问器的主体实现。这类方法、运算符或访问器的体受以下约束的控制:
不允许不安全块。
yield return 语句不能放在 try-catch 块中的任何位置。该语句可放在后跟 finally 块的 try 块中。
yield break 语句可放在 try 块或 catch 块中,但不能放在 finally 块中。
yield 语句不能出现在匿名方法中。有关更多信息,请参见匿名方法(C# 编程指南)。
当和 expression 一起使用时,yield return 语句不能出现在 catch 块中或含有一个或多个 catch 子句的 try 块中。
对于”yield”这个关键字我已经见过N次了,直到最近我才知道这个关键字所蕴含的力量。我将在下面展示出一些使用”yield”让你的代码有更高可读性和更好性能的例子.
为了让你对yield有一些快速概览,我首先要展示一个没有使用这个关键字的例子,下面的代码很简单,但在我最近的项目中却很常见
IList<string> FindBobs(IEnumerable<string> names)
{
var bobs = new List<string>();
foreach(var currName in names)
{
if(currName == "Bob")
bobs.Add(currName);
}
return bobs;
}
注意在这里我使用IEnumerable<string>作为参数类型并以IList<string>作为返回类型,通常来说,我更倾向于在参数输入的类型方面的范围越宽越好,但在返回类型上面更加严格(译者按:即输入时多用基类或接口,返回时用子类或实现类),对于输入来说,如果你需要用foreach来对其进行循环的话,使用IEnumerable会更有意义。而对于输出(译者按:也就是返回),我使用接口来让实现部分可以改变。在这里我想让调用者省去生成列表的麻烦,所以我选择list作为返回类型.
而问题在于,我的设计并不具有可链接性,这样的设计需要产生列表作为返回值,实现上,这个列表或许不会很大,但这并不必要
现在,让我们来看看以“yield”的方式来做这些,而后我会解释如何使用它,以及它工作的原理。
IEnumerable<string> FindBobs(IEnumerable<string> names)
{
foreach(var currName in names)
{
if(currName == "Bob")
yield return currName;
}
}
在这个版本中,我们将返回类型改为IEnumerable,并且我们使用”yield return”.注意我再也不需要创建一个列表,现在是不是有些迷惑的?别着急,在理解它的工作方式的情况它会变的越来越简单.
当使用”yield return”关键词组时,.net会为你生成一大串管道代码,你可以尽管假装这是个魔法。当开始在被调用的代码中循环时(这里不是list),实现上发生的是这个函数被一遍一遍的调用,但每一次都从上一次执行退出的部分开始继续执行.
传统的执行方法
- 调用函数
- 函数执行并返回list
- 调用部分使用返回的list
Yield的执行方法
- 调用函数
- 调用者请求item
- 下一个item返回
- 回到步骤2
虽然yield执行的实现貌似有些复杂,但我们最终只需要一次“弹出”一个item,而不是创建整个list并返回.
对于句法说,我个人认为yield更加简洁,并且对于传递函数的用途表现的更好(译者按:也就是代码可读性),我使用IEnumerable作为返回类型来通知调用者它可以被foreach循环并且返回数据,而调用者现在可以自己决定它是否愿意将返回值存放到列表中,即使这会以性能作为代价。
在我提供的这个简单例子中,也许你并不能发现很多使用yield的好处,然而,你可以在调用者需要取消遍历所有的函数提供的内容时节省很多不必要的工作,当你在方法链接时使用yield,你可以省下的工作(时间)或许会成倍叠加。
Ayende已经有了很棒的例子:using yield for a slick pipes & filters implementation,他甚至还讲述了:version that is multi-threaded。这让我觉得非常有趣.
最开始我对yield的保留意见是使用这个关键字或许会导致潜在的性能问题,但实际上,至今为止我还未能发现任何信息来说明关于yield对性能的影响,而我在上面提到提高性能的地方远远大于编译器overhead那部分。
小结
Yield可以让你的代码更加高效并拥有更高的可读性,已经是.net 2.0时代了,我想已经没有什么借口可以阻止我们学习和使用yield.
---------------------------------------------------------------------
译者:这篇文章是我翻译的十个鲜为人知的C#关键字里的pangxiaoliang[北京]流浪者同学在评论中提出的意见,那篇文章只是做了个总结,我发现已经有关于论述更深层次的好文了,所以我就不重复发明轮子直接进行翻译了,在翻译的过程中我也学到了不少.谢谢
原文链接:http://www.ytechie.com/2009/02/using-c-yield-for-readability-and-performance.html
[转]C#中yield用法的更多相关文章
- python中yield用法
在介绍yield前有必要先说明下Python中的迭代器(iterator)和生成器(constructor). 一.迭代器(iterator) 在Python中,for循环可以用于Python中的任何 ...
- C#中yield用法
yield 关键字向编译器指示它所在的方法是迭代器块.编译器生成一个类来实现迭代器块中表示的行为.在迭代器块中,yield 关键字与 return 关键字结合使用,向枚举器对象提供值.这是一个返回值, ...
- unity 3d yield 用法总结
最近,需要需要用unity 3d做点东西,但是了碰到了延迟加载问题,我总结余下: Coroutines & Yield是unity3d编程中重要的概念,它可以实现将一段程序延迟执行或者将其各个 ...
- Python yield用法浅析(stackoverflow)
这是stackoverflow上一个关于python中yield用法的帖子,这里翻译自投票最高的一个回答,原文链接 here 问题 Python中yield关键字的用途是什么?它有什么作用?例如,我试 ...
- C#中yield return用法分析
这篇文章主要介绍了C#中yield return用法,对比使用yield return与不使用yield return的流程,更直观的分析了yield return的用法,需要的朋友可以参考下. 本文 ...
- Python中yield和yield from的用法
yield python中yield的用法很像return,都是提供一个返回值,但是yield和return的最大区别在于,return一旦返回,则代码段执行结束,但是yield在返回值以后,会交出C ...
- nodejs中yield的用法?
nodejs中yield的用法? https://www.zhihu.com/question/32752866?sort=created
- Python Deque 模块使用详解,python中yield的用法详解
Deque模块是Python标准库collections中的一项. 它提供了两端都可以操作的序列, 这意味着, 你可以在序列前后都执行添加或删除. https://blog.csdn.net/qq_3 ...
- python生成器中yield和send分析
生成器 在python中生成器是指用代码实现迭代器的的功能本质还是迭代器,只不过是代码实现迭代器功能.在python中生成器是由函数实现的,通常我们在函数中加入yeild就可以实现生成器. 生成器中y ...
随机推荐
- C++11的新特性lambda的小试牛刀RAII
C/C++的资源是手动管理的 这导致程序员在申请资源时,最后用完了偶尔会忘记回收 C++语言的发明者倡导RAII,资源获取即初始化 使用对象来管理资源的生命周期,在超出作用域时,析构函数自动释放资源 ...
- SQLSERVER 中GO的作用详解
具体不废话了,请看下文详解. ? 1 2 3 4 5 6 7 8 9 10 use db_CSharp go select *, 备注=case when Grade>=90 then ' ...
- iOS内存管理系列之一:对象所有权与引用计数
当一个所有者(owner,其本身可以是任何一个Objective-C对象)做了以下某个动作时,它拥有对一个对象的所有权(ownership): 1. 创建一个对象.包括使用任何名称中包含“alloc” ...
- JdbcTemplate 操作Oracle Blob
1:增加操作 public int addTest(TestVo tv) { byte bz[] = tv.getBz().getBytes(); LobHandler lobHandler = ne ...
- NWERC 2012 Problem I Idol
又是个2-sat的模板题: 反正评委的选择必须有一个是正确的,1错误,那么2就必须正确: 这就是一个2-sat问题. 直接上白书的模板啊,不过稍微要注意的一点是对于第一个点必须要选择,不然就违反了题意 ...
- 提升 DevOps 效率,试试 ChatOps 吧!
本文翻译自文章 To Boost DevOps, Try ChatOps,文中用简单易懂的方式介绍了 ChatOps 的发展和价值,由 OneAPM 工程师编译整理. 当我们谈论 DevOps 时,总 ...
- 如何修改Ubuntu双系统的默认开机启动项
转自:http://jingyan.baidu.com/article/ed15cb1b05d0311be369818a.html 对 于安装Ubuntu系统和windows系统(windows xp ...
- maven错误解决:编码GBK的不可映射字符
直接将项目改为UTF-8编码,无效! 要通过修改pom.xml文件,告诉maven这个项目使用UTF-8来编译. 方案一: 在pom.xml的/project/build/plugins/下的编译插件 ...
- unity3d Human skin real time rendering with blood and water drop effect真实模拟人皮实时渲染之血液和水珠掉落效果
在之前的一篇(链接在此)文章中写了下关于真实模拟皮肤渲染,在此基础之上又想加上血液效果,在洗澡的时候(= =:)又想在skin上加上水珠的效果,所以研究了下,做出来效果感觉还不错,放下效果图: 水珠 ...
- UVa816 Abbott's Revenge
Abbott's Revenge Time limit: 3.000 seconds Abbott’s Revenge Abbott’s Revenge The 1999 World FinalsC ...