c# yield提高代码性能和可读性

对于”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那部分。

(0)

相关推荐

  • C#中yield用法使用说明

    在迭代器块中用于向枚举数对象提供值或发出迭代结束信号.它的形式为下列之一: yield return <expression>; yield break; 备注: 计算表达式并以枚举数对象值的形式返回:expression 必须可以隐式转换为迭代器的 yield 类型. yield 语句只能出现在 iterator 块中,该块可用作方法.运算符或访问器的体.这类方法.运算符或访问器的体受以下约束的控制:不允许不安全块. 方法.运算符或访问器的参数不能是 ref 或 out. yield 语句不

  • C#:foreach与yield语句的介绍

    1. foreach语句 C#编译器会把foreach语句转换为IEnumerable接口的方法和属性. 复制代码 代码如下: foreach (Person p in persons) { Console.WriteLine(p); } foreach语句会解析为下面的代码段. •调用GetEnumerator()方法,获得数组的一个枚举•在while循环中,只要MoveNext()返回true,就一直循环下去•用Current属性访问数组中的元素 复制代码 代码如下: IEnumerator

  • C#通过yield实现数组全排列的方法

    本文实例讲述了C#通过yield实现数组全排列的方法.分享给大家供大家参考.具体分析如下: 从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列.当m=n时所有的排列情况叫全排列. static void Swap<T>(ref T a, ref T b) { T t = a; a = b; b = t; } static IEnumerable<int[]> Perm(int[] arr, int pos) { if (pos

  • C# yield在WCF中的错误用法(一)

    在定义API的时候,对于一些返回集合对象的方法,很多人喜欢将返回类型定义成IEnumerable<T>,这本没有什么问题.这里要说的是另一个问题:对于返回类型为IEnumerable<T>的方法来说,我们可以使用yield return的方式来输出返回集合的元素.但是如果我们不了解yield 关键字背后的实现机制,很有可能造成很大的问题. 这是一个WCF相关的问题,我想99%的人都有可能会犯这样的错误--即使你对yield了解得非常透彻.闲话少说,我们通过一个简单的实例来说明这个问

  • C#使用yield关键字让自定义集合实现foreach遍历的方法

    foreach遍历是C#常见的功能,而本文通过实例形式展现了C#使用yield关键字让自定义集合实现foreach遍历的方法.具体步骤如下: 一般来说当我们创建自定义集合的时候为了让其能支持foreach遍历,就只能让其实现IEnumerable接口(可能还要实现IEnumerator接口) 但是我们也可以通过使用yield关键字构建的迭代器方法来实现foreach的遍历,且自定义的集合不用实现IEnumerable接口 注意:虽然不用实现IEnumerable接口 ,但是迭代器的方法必须命名为

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

    从0遍历到20(不包括20),输出遍历到的每个元素,并将大于2的所有数字放到一个IEnumerable<int>中返回 解答1:(我以前经常这样做) static IEnumerable<int> WithNoYield() { IList<int> list = new List<int>(); for (int i = 0; i < 20; i++) { Console.WriteLine(i.ToString()); if(i > 2) l

  • C# yield在WCF中的错误使用(二)

    昨天写了<yield在WCF中的错误使用--99%的开发人员都有可能犯的错误[上篇]>,引起了一些讨论.关于yield关键字这个语法糖背后的原理(C#编译器将它翻译成什么)其实挺简单,虽然有时候因为误用它会导致一些问题,但是它本无过错.接下来,我们通过这篇短文简单地谈谈我所理解的yield. 目录 一.先看一个简单的例子 二.了解本质,只需要看看yield最终编译成什么 三.回到WCF的例子 一.先看一个简单的例子 我们现在看一个简单的例子.我们在一个Console应用中编写了如下一段简单的程

  • C# yield关键字详解

    对于yield关键字我们首先看一下msdn的解释: 如果你在语句中使用 yield 关键字,则意味着它在其中出现的方法.运算符或 get 访问器是迭代器. 通过使用 yield 定义迭代器,可在实现自定义集合类型的 IEnumerable和 IEnumerator模式时无需其他显式类(保留枚举状态的类,有关示例,请参阅 IEnumerator<T>). yield是一个语法糖 看msdn 的解释总是让人感觉生硬难懂.其实yield关键字很好理解.首先我们对于性质有个了解.yield是一个语法糖

  • C#中yield return用法分析

    本文实例讲述了C#中yield return用法,并且对比了使用yield return与不使用yield return的情况,以便读者更好的进行理解.具体如下: yield关键字用于遍历循环中,yield return用于返回IEnumerable<T>,yield break用于终止循环遍历. 有这样的一个int类型的集合: static List<int> GetInitialData() { return new List<int>(){1,2,3,4}; }

  • C#中的yield关键字的使用方法介绍

    yield不能单独放在try-catch块中,如果try中有yield那么,这个try块后面不许跟着finally块:也不能出现在匿名方法中,所以,看起来yield似乎并不常用,但是也不是不用.我前面有一个关于迭代器的例子<C#中的迭代器基础>中就用到了.可以参考一下那个例子,但是这里要再说的一点是我后来看到的,yield是跟return一起使用的,形式为yield return xxx,一般来说单独的return在每个方法中只能存在一个.而yield则不同的是,可以出现连续多个.迭代器,是一

随机推荐