当前位置: 首页 > news >正文

c# yield关键字原理详解

c# yield关键字的用法

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

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

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

    static void Main(string[] args)
    {
        //通过foreach循环迭代此函数
        foreach(int item in enumerableFuc())
        {
            WriteLine(item);
        }
        ReadKey();
    }
}

输出结果:
1
2
3

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

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

    static void Main(string[] args)
    {
        //通过foreach循环迭代此函数
        foreach(int item in enumerableFuc())
        {
            WriteLine(item);
        }
        ReadKey();
    }
}

输出结果:
1
2

2.只能使用在返回类型必须为 IEnumerable、IEnumerable<T>、IEnumerator 或 IEnumerator<T>的方法、运算符、get访问器中。

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

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

    static void Main(string[] args)
    {
        //用while循环代替foreach
        IEnumerator<int> enumerator = enumerableFuc().GetEnumerator();
        while (enumerator.MoveNext())
        {
            int current = enumerator.Current;
            WriteLine(current);
        }
        ReadKey();
    }
}

输出结果:
1
2
3

至于为什么会出现这种情况,我们可以用ILSpy对生成的exe进行反编译来找到原因。
由于直接反编译成C#会变为原样
1362861-20180330213029351-1687680873.png
所以我们选择反编译为带C#注释的IL代码,虽然可读性差点,但是可以详细的了解其中过的原理。
先来看Program翻译的情况,编译的时候自动生成了一个新的类。
1362861-20180330213049880-385412589.png
接下来我们来仔细看这些代码,EnumerableFuc()返回了这个新的类。
1362861-20180330213100922-1104372785.png
看这个代码自动生成的类的实现,发现它继承了IEnumerable、IEnumerable<T>、IEnumerator 或 IEnumerator<T>,这时我们应该已经能猜到这个新的类就是我们没有实现对应的IEnumerator的MoveNext(),和Current属性,但是我们仍然能正常使用这些函数的原因了。
1362861-20180330213109477-1874000904.png
我们再来看一下这个类具体是如何实现迭代的呢,我们主要来看一下MoveNext()函数
1362861-20180330213118539-889684123.png

1362861-20180330213123695-1890914850.png
每次调用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接口实现的迭代功能。

转载于:https://www.cnblogs.com/blueberryzzz/p/8678700.html

相关文章:

  • 一个日期处理类库moment.js
  • 使用Kolla构建Pike版本OpenStack Docker镜像
  • spring MVC 使用 hibernate validator验证框架,国际化配置
  • Kubernetes软件包管理系统-Helm架构
  • A - 夹角有多大(题目已修改,注意读题)
  • 卸载openssl后yum无法使用,ssh无法连接的解决办法
  • 春雪
  • (16)Reactor的测试——响应式Spring的道法术器
  • Oracle 谈 JavaFX 及 Java 客户端技术的未来
  • [译] React 中的受控组件和非受控组件
  • drbd配置简述
  • 聊聊编译时注解
  • 微服务架构—高级设计篇
  • 漫谈版本控制系统
  • pandas(一)操作Series和DataFrame的基本功能
  • [ 一起学React系列 -- 8 ] React中的文件上传
  • 【391天】每日项目总结系列128(2018.03.03)
  • 【腾讯Bugly干货分享】从0到1打造直播 App
  • Create React App 使用
  • httpie使用详解
  • iOS 颜色设置看我就够了
  • Netty 框架总结「ChannelHandler 及 EventLoop」
  • vue2.0一起在懵逼的海洋里越陷越深(四)
  • webpack项目中使用grunt监听文件变动自动打包编译
  • 简析gRPC client 连接管理
  • 盘点那些不知名却常用的 Git 操作
  • 如何使用 OAuth 2.0 将 LinkedIn 集成入 iOS 应用
  • 适配iPhoneX、iPhoneXs、iPhoneXs Max、iPhoneXr 屏幕尺寸及安全区域
  •  一套莫尔斯电报听写、翻译系统
  • 原创:新手布局福音!微信小程序使用flex的一些基础样式属性(一)
  • 怎么将电脑中的声音录制成WAV格式
  • C# - 为值类型重定义相等性
  • Prometheus VS InfluxDB
  • Salesforce和SAP Netweaver里数据库表的元数据设计
  • ​2021半年盘点,不想你错过的重磅新书
  • ​RecSys 2022 | 面向人岗匹配的双向选择偏好建模
  • ###51单片机学习(2)-----如何通过C语言运用延时函数设计LED流水灯
  • #define MODIFY_REG(REG, CLEARMASK, SETMASK)
  • #LLM入门|Prompt#2.3_对查询任务进行分类|意图分析_Classification
  • (1)(1.9) MSP (version 4.2)
  • (4)STL算法之比较
  • (Matlab)使用竞争神经网络实现数据聚类
  • (Repost) Getting Genode with TrustZone on the i.MX
  • (附源码)springboot青少年公共卫生教育平台 毕业设计 643214
  • (附源码)ssm失物招领系统 毕业设计 182317
  • (剑指Offer)面试题41:和为s的连续正数序列
  • (每日持续更新)jdk api之FileReader基础、应用、实战
  • (牛客腾讯思维编程题)编码编码分组打印下标题目分析
  • (十一)JAVA springboot ssm b2b2c多用户商城系统源码:服务网关Zuul高级篇
  • (四)TensorRT | 基于 GPU 端的 Python 推理
  • (一)SpringBoot3---尚硅谷总结
  • (转)nsfocus-绿盟科技笔试题目
  • (转)程序员疫苗:代码注入
  • (转)用.Net的File控件上传文件的解决方案
  • ****** 二 ******、软设笔记【数据结构】-KMP算法、树、二叉树