这几天在Python程序员的微信订阅号中总是见到yield的关键字,才想起来在C#中也是有yield,但是只是知道有,从来没有了解过他的用法,今天有时间就来看看是怎么使用的。刚开始肯定就是搜索一下用法了,找到两篇说明示例,一是 C# 中的"yield"使用,第二个是MSDN的官方api yield(C# 参考)

说实话第一个示例看完还是很模糊的概念,例子也没有看懂是在干嘛,一直到MSDN中给出结果集我才明白了到底的用法是怎么样的。

先来举例一个需求: 一个方法返回一个IEnumerable 类型结果集(例如返回一个list<int>的结果),通常的代码是这样的

         /// <summary>
///
/// </summary>
/// <returns></returns>
public IEnumerable<int> Method()
{
List<int> results = new List<int>();
int counter = ;
int result = ; while (counter++ < )
{
result = result * ;
results.Add(result);
}
return results;
}

这样就完成了需求。

但是 有了yield之后就可以这样写了

         /// <summary>
///
/// </summary>
/// <returns></returns>
public IEnumerable<int> YieldDemo()
{
int counter = ;
int result = ;
while (counter++ < )
{
result = result * ;
yield return result;
}
}

两种效果是一样的,但是从我个人而言我喜欢第二个,感觉更简洁一些。

题外话:在写这两个例子中我又增加了一个知识点

返回值IEnumerable其实和IEnumerable<object>是等价的,只是IEnumerable的结果需要动态的解析;

但是还搞不清楚这两者实现有什么区别,所以我想看看这两个在做同一件事的时候效率如何,下面来尝试使用while循环10000000的取数据的耗时比较

使用 BenchmarkDotNet 测试结果如下

使用Stopwatch测试结果也是一样的

从这个测试里面可以看出YieldDemo方法几乎没有耗时,但是实际情况是不可能的吧,所以我又尝试做了遍历的测试

             Stopwatch stop = new Stopwatch();
stop.Start();
var res = new YieldTest().YieldDemo();
foreach (var item in res)
{ }
var a = stop.ElapsedMilliseconds;
stop.Restart(); var rrrrr = new YieldTest().Method();
foreach (var item in rrrrr)
{ }
var b = stop.ElapsedMilliseconds;
stop.Restart();

这个测试的结果是a=168,b=142.对比上一个测试结果让我更加疑惑,我就开始打断点,看看执行的顺序是怎样的。

结果如下:  

     在 第三行 断点压根就没有进YieldDemo这个方法,而是当进行foreach 遍历结果的时候,才开始进入了YieldDemo这个方法,更奇怪的是每次的foreach 都会进入YieldDemo的while一次去取数据

这个结果让我有点懵了,只能再仔细看看文档解析,

迭代器方法运行到 yield return 语句时,会返回一个 expression,并保留当前在代码中的位置。 下次调用迭代器函数时,将从该位置重新开始执行。 可以使用 yield break 语句来终止迭代。

貌似这里面是涉及到了迭代器的东西。马上找迭代器的知识点,在 详解C# 迭代器 中看到这样一句解释

需要强调的一点是,对于迭代块,虽然我们写的方法看起来像是在顺序执行,实际上我们是让编译器来为我们创建了一个状态机。这就是在C#1中我们书写的那部分代码---调用者每次调用只需要返回一个值,因此我们需要记住最后一次返回值时,在集合中位置。

当编译器遇到迭代块时,它创建了一个实现了状态机的内部类。这个类记住了我们迭代器的准确当前位置以及本地变量,包括参数。

这句话貌似解析了上面的疑问,但是看的有点云里雾里,还要花时间消化一下里面的具体原理 。

官方提示使用yield有一些限制,需要注意

 不能将 yield return 语句置于 try-catch 块中。 可将 yield return 语句置于 try-finally 语句的 try 块中。
可将 yield break 语句置于 try 块或 catch 块中,但不能将其置于 finally 块中。
如果 foreach 主体(在迭代器方法之外)引发异常,则将执行迭代器方法中的 finally 块。
匿名方法。 有关详细信息,请参阅匿名方法。
包含不安全的块的方法。 有关详细信息,请参阅unsafe。

针对第一点让我感觉使用好有限制,为啥不能在try-catch 中使用呢?

关于其他的一些使用方法在MSDN里面都有详细的讲解,感觉没有什么好多说的。

初次使用C#中的yield的更多相关文章

  1. 可惜Java中没有yield return

    项目中一个消息推送需求,推送的用户数几百万,用户清单很简单就是一个txt文件,是由hadoop计算出来的.格式大概如下: uid caller 123456 12345678901 789101 12 ...

  2. C#中的yield return与Unity中的Coroutine(协程)(上)

    C#中的yield return C#语法中有个特别的关键字yield, 它是干什么用的呢? 来看看专业的解释: yield 是在迭代器块中用于向枚举数对象提供值或发出迭代结束信号.它的形式为下列之一 ...

  3. 关于Python中的yield

    关于Python中的yield   在介绍yield前有必要先说明下Python中的迭代器(iterator)和生成器(constructor). 一.迭代器(iterator) 在Python中,f ...

  4. 关于Python中的yield(转载)

    您可能听说过,带有 yield 的函数在 Python 中被称之为 generator(生成器),何谓 generator ? 我们先抛开 generator,以一个常见的编程题目来展示 yield ...

  5. C#中的yield return用法演示源码

    下边代码段是关于C#中的yield return用法演示的代码. using System;using System.Collections;using System.Collections.Gene ...

  6. python 中的 yield 究竟为何物?生成器和迭代器的区别?

    当你突然看到别人的代码中出现了一个好像见过但又没用过的关键词 比如 yield ,你是否会觉得这段代码真是高大上呢? 或许只有我这种小白才会这样子觉得,就在刚刚,我就看见了别人的代码中的yield,觉 ...

  7. Python中的yield生成器的简单介绍

    Python yield 使用浅析(整理自:廖 雪峰, 软件工程师, HP 2012 年 11 月 22 日 ) 初学 Python 的开发者经常会发现很多 Python 函数中用到了 yield 关 ...

  8. 在 PHP 中使用 `yield` 来做内存优化

    你有没有想过 "在 PHP 中使用 yield 会有什么益处",我将为你节省一些谷歌搜索的时间: 我列出了一些要向你介绍的要点来全面认知 yield: 什么是 yield. yie ...

  9. 深入理解Python中的yield和send

    send方法和next方法唯一的区别是在执行send方法会首先把上一次挂起的yield语句的返回值通过参数设定,从而实现与生成器方法的交互. 但是需要注意,在一个生成器对象没有执行next方法之前,由 ...

随机推荐

  1. android中实现view可以滑动的六种方法续篇(二)

    承接上一篇,上一篇中讲解了实现滑动的第五种方法,如果你还没读过,可点击下面链接: http://www.cnblogs.com/fuly550871915/p/4985482.html 这篇文章现在来 ...

  2. IOS沙盒Files目录说明和常用操作

    Ios沙盒目录清单 1 Documents 用于存储用户生成的文件.其他数据及其他程序不能重新创建的文件,默认文件通过iCloud自动备份.如果不需要iCloud备份,则设置标记 NSURLIsExc ...

  3. tmpFile.renameTo(classFile) failed 错误

    突然的出现了这个tmpFile.renameTo(classFile) failed 错误. 也许是我删掉了tomcat里面的webapps 中的项目,而通过debug部署,而出现了这个问题. 一开始 ...

  4. Storm之spout,bolt编写

    Storm,核心代码使用clojure书写,实用程序使用python开发,使用java开发拓扑. Nimbus节点接收到请求,对提交的拓扑进行分片,分成一个个的task,并将task和supervis ...

  5. [编]IoT The Internet of Things (IoT) 物联网

    物联网是新一代信息技术的重要组成部分.其英文名称是“The Internet of things”.由此,顾名思义,“物联网就是物物相连的互联网”.这有两层意思:第一,物联网的核心和基础仍然是互联网, ...

  6. POJ-1979 Red and Black(DFS)

    题目链接:http://poj.org/problem?id=1979 深度优先搜索非递归写法 #include <cstdio> #include <stack> using ...

  7. Mybatis框架_part1

    mybatis介绍 mybatis就是一个封装了jdbc的持久层框架,它的前身是ibatis.Mybatis与hibernate一样都是持久层框架,但是它与hibernate不同的是,它不是一个完全的 ...

  8. css中table tr:nth-child(even)改变tr背景颜色: IE7,8无效

    例如: .my_table tr:nth-child(even){ background:#E6EDF5; } .my_table tr:nth-child(odd){ background:#F0F ...

  9. [转]在Eclipse中使用JUnit4进行单元测试(高级篇)

    通过前2篇文章,您一定对JUnit有了一个基本的了解,下面我们来探讨一下JUnit4中一些高级特性. 一.高级Fixture 上一篇文章中我们介绍了两个Fixture标注,分别是@Before和@Af ...

  10. getBoundingClientRect详解

    getBoundingClientRect获取的left和top始终是该元素相对于页面原点坐标的位置,当它大于原点坐标则为正数,等于原点坐标为零,小于为负数.