这几天在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. 完全卸载VS2005或VS2008的步骤

    手动卸载步骤: Visual Studio Express Editions 进入控制面板,运行添加或删除程序  卸载 "MSDN Library for Visual Studio 200 ...

  2. rpm安装rpm-package报错:Header signature NOKEY 和 error: Failed dependencies:

    以前经常遇到这个问题,一直未有记录,今天记录下来: 在安装rpm包的时候报错误如下: Question 1: warning: *.rpm: Header V3 DSA signature: NOKE ...

  3. GUID分区与MBR分区

    1.MBR分区表类型的磁盘主引导记录(Master Boot Record,缩写:MBR),又叫做主引导扇区,它仅仅包含一个64个字节的硬盘分区表.由于每个分区信息需要16个字节,所以对于采用MBR型 ...

  4. 怎样用好ZBrush中的PaintStop插件

    PaintStop是ZBrush®3.1的手绘插件,可以比较真实的模拟手绘风格,尤其是用水彩笔刷画水墨风格画.PaintStop插件可供用户免费使用. PaintStop是一款功能强大的插件,已经被添 ...

  5. 边工作边刷题:70天一遍leetcode: day 80

    Palindrome Permutation I/II 要点: oddCount to increase/decrease count II: chars: 先统计,再得到一半的c,相同的在一起,所以 ...

  6. 边工作边刷题:70天一遍leetcode: day 82-1

    Closest Binary Search Tree Value II 要点:通过iterator,把closest值附近的k个closest找到,从而time降为O(klgn) in order i ...

  7. 最长上升子序列(LIS)模板

    最长递增(上升)子序列问题:在一列数中寻找一些数,这些数满足:任意两个数a[i]和a[j],若i<j,必有a[i]<a[j],这样最长的子序列称为最长递增(上升)子序列. 考虑两个数a[x ...

  8. Android配置----Eclipse+BlueStacks调试Android应用

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/3 ...

  9. Unity3D实现赛车的灯光效果

    车灯的需求 在赛车游戏中,遇到灯光弱的环境,赛车车可以打开前车灯照亮路边及前方,那一定是非常酷! 也见过虚拟现实项目通过Unity模拟汽车车灯的效果,但是我还没有想到好的思路来实现. 我的思路 使用( ...

  10. git冲突解决办法

    git错误error: Your local changes to the following files would be overwritten 然后可以使用git diff -w +文件名 来确 ...