架构师_程序员

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 152|回复: 0

[资料] c# yield关键字的用法

[复制链接]
跳转到指定楼层
楼主
发表于 2019-5-29 09:55:11
zu
1.yield实现的功能

yield return:

先看下面的代码,通过yield return实现了类似用foreach遍历数组的功能,说明yield return也是用来实现迭代器的功能的。

  1. using static System.Console;
  2. using System.Collections.Generic;

  3. class Program
  4. {
  5.     //一个返回类型为IEnumerable<int>,其中包含三个yield return
  6.     public static IEnumerable<int> enumerableFuc()
  7.     {
  8.         yield return 1;
  9.         yield return 2;
  10.         yield return 3;
  11.     }

  12.     static void Main(string[] args)
  13.     {
  14.         //通过foreach循环迭代此函数
  15.         foreach(int item in enumerableFuc())
  16.         {
  17.             WriteLine(item);
  18.         }
  19.         ReadKey();
  20.     }
  21. }

  22. 输出结果:
  23. 1
  24. 2
  25. 3
复制代码
yield break:

再看下面的代码,只输出了1,2,没有输出3,说明这个迭代器被yield break停掉了,所以yield break是用来终止迭代的。

  1. using static System.Console;
  2. using System.Collections.Generic;
  3. class Program
  4. {
  5.     //一个返回类型为IEnumerable<int>,其中包含三个yield return
  6.     public static IEnumerable<int> enumerableFuc()
  7.     {
  8.         yield return 1;
  9.         yield return 2;
  10.         yield break;
  11.         yield return 3;
  12.     }

  13.     static void Main(string[] args)
  14.     {
  15.         //通过foreach循环迭代此函数
  16.         foreach(int item in enumerableFuc())
  17.         {
  18.             WriteLine(item);
  19.         }
  20.         ReadKey();
  21.     }
  22. }

  23. 输出结果:
  24. 1
  25. 2
复制代码
2.只能使用在返回类型必须为 IEnumerable、IEnumerable<T>、IEnumerator 或 IEnumerator<T>的方法、运算符、get访问器中。

3.yield关键字的实现原理

我们用while循环代替foreach循环,发现我们虽然没有实现GetEnumerator(),也没有实现对应的IEnumerator的MoveNext(),和Current属性,但是我们仍然能正常使用这些函数。

  1. class Program
  2. {
  3.     //一个返回类型为IEnumerable<int>,其中包含三个yield return
  4.     public static IEnumerable<int> enumerableFuc()
  5.     {
  6.         yield return 1;
  7.         yield return 2;
  8.         yield return 3;
  9.     }

  10.     static void Main(string[] args)
  11.     {
  12.         //用while循环代替foreach
  13.         IEnumerator<int> enumerator = enumerableFuc().GetEnumerator();
  14.         while (enumerator.MoveNext())
  15.         {
  16.             int current = enumerator.Current;
  17.             WriteLine(current);
  18.         }
  19.         ReadKey();
  20.     }
  21. }

  22. 输出结果:
  23. 1
  24. 2
  25. 3
复制代码
至于为什么会出现这种情况,我们可以用ILSpy对生成的exe进行反编译来找到原因。

由于直接反编译成C#会变为原样



所以我们选择反编译为带C#注释的IL代码,虽然可读性差点,但是可以详细的了解其中过的原理。
先来看Program翻译的情况,编译的时候自动生成了一个新的类。



接下来我们来仔细看这些代码,EnumerableFuc()返回了这个新的类。



看这个代码自动生成的类的实现,发现它继承了IEnumerable、IEnumerable<T>、IEnumerator 或 IEnumerator<T>,这时我们应该已经能猜到这个新的类就是我们没有实现对应的IEnumerator的MoveNext(),和Current属性,但是我们仍然能正常使用这些函数的原因了。



我们再来看一下这个类具体是如何实现迭代的呢,我们主要来看一下MoveNext()函数





每次调用MoveNext()函数都会将state加1,一共进行了4次迭代,前三次返回true,最后一次返回false,代表迭代结束。这四次迭代对应被3个yield return语句分成4部分的enumberableFuc()中的语句。

用enumberableFuc()来进行迭代的真实流程就是:

1.运行enumberableFuc()函数,获取代码自动生成的类的实例。
2.接着调用GetEnumberator()函数,将获取的类自己作为迭代器开始迭代。
3.每次运行MoveNext(),state增加1,通过switch语句可以让每次调用MoveNext()的时候执行不同部分的代码。
4。MoveNext()返回false,结束。
这也能说明yield关键字其实是一种语法糖,最终还是通过实现IEnumberable<T>、IEnumberable、IEnumberator<T>和IEnumberator接口实现的迭代功能。





上一篇:.NET弹性和瞬态故障处理Polly重试策略
下一篇:HttpResult获取的是set-cookie而不是cookie
帖子永久地址: 

架构师_程序员 - 论坛版权1、本主题所有言论和图片纯属会员个人意见,与本论坛立场无关
2、本站所有主题由该帖子作者发表,该帖子作者与架构师_程序员享有帖子相关版权
3、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者和架构师_程序员的同意
4、帖子作者须承担一切因本文发表而直接或间接导致的民事或刑事法律责任
5、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责
6、如本帖侵犯到任何版权问题,请立即告知本站,本站将及时予与删除并致以最深的歉意
7、架构师_程序员管理员和版主有权不事先通知发贴者而删除本文

码农网,只发表在实践过程中,遇到的技术难题,不误导他人。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

免责声明:
码农网所发布的一切软件、编程资料或者文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。

Mail To:help@itsvse.com

QQ|Archiver|手机版|小黑屋|架构师 ( 鲁ICP备14021824号-2 )|网站地图

GMT+8, 2019-7-18 08:51

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表