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

[C#] 基于 yield 语句的迭代器逻辑懒执行

众所周知, C# 可以通过 yield 语句来快速向 IEnumerator 或者 IEnumerable 类型的方法返回值返回一个元素. 但它还有另外一个特性, 就是其内部逻辑的懒执行. 每两个 yield 语句之间的逻辑都是一个状态, 只有在调用迭代器的 MoveNext 方法后, 才会执行下一个状态的逻辑.

在文章中, 编译后的代码已经经过简化和删减, 以便于理解


迭代器方法的懒执行

举一个简单的例子:

IEnumerator SomeLogic()
{Console.WriteLine("hello world");yield return null;Console.WriteLine("fuck you world");
}

在调用的时候, 逻辑并不会被立即执行, 只有对其返回的迭代器调用 MoveNext 的时候, 才会继续执行.

var enumerator = SomeLogic();enumerator.MoveNext();   // 打印 hello world
enumerator.MoveNext();   // 打印 fuck you world

迭代器方法的编译

在 C# 中, 使用了 yield 语句的, 返回 IEnumerator 或 IEnumerable 的方法, 会由编译器生成一个迭代器或可迭代类型, 在类型的内部包含该方法的逻辑.

例如上面提到的代码, 它会被编译成大概这样:

IEnumerator SomeLogic()
{return new SomeLogicEnumerator();
}private sealed class SomeLogicEnumerator : IEnumerator, IDisposable
{private int state;private object current;object IEnumerator.Current{get{return current;}}public SomeLogicEnumerator(int state){this.state = state;}void IDisposable.Dispose(){}private bool MoveNext(){int num = state;if (num != 0){if (num != 1){return false;}state = -1;Console.WriteLine("AWA");return false;}state = -1;Console.WriteLine("QWQ");current = null;state = 1;return true;}bool IEnumerator.MoveNext(){//ILSpy generated this explicit interface implementation from .override directive in MoveNextreturn this.MoveNext();}void IEnumerator.Reset(){throw new NotSupportedException();}
}

我们可以看到, 在这个迭代器类型中, 有一个 state 字段存储了当前的状态, 而在 MoveNext 被调用时, 会切换当前状态, 然后根据当前状态执行对应逻辑.

当然, 如果你的迭代器逻辑稍微长一些, 它也是可以处理的.

IEnumerator SomeLogic()
{Console.WriteLine("QWQ");yield return 1;Console.WriteLine("AWA");yield return 2;Console.WriteLine("QJFD");yield return 3;Console.WriteLine("JWOEIJFOIWE");
}

它的 MoveNext 就变成了这样:

private bool MoveNext()
{switch (state){case 0:Console.WriteLine("QWQ");current = 1;state = 1;return true;case 1:Console.WriteLine("AWA");current = 2;state = 2;return true;case 2:Console.WriteLine("QJFD");current = 3;state = 3;return true;case 3:state = -1;Console.WriteLine("JWOEIJFOIWE");return false;default:return false;}
}

相关文章:

  • ERC20 | ERC-20/ERC-721/ERC-1155/ERC-3525 区别
  • 如何进行更好的面试回复之缓存函数在项目中的性能优化?
  • 使用 db2diag 工具来分析 db2diag 日志文件
  • Elasticsearch的Snapshot and Restore(快照备份与恢复)
  • 物联网后端个人第十四周总结
  • AI 绘画Stable Diffusion 研究(十一)sd图生图功能详解-美女换装
  • 美颜SDK算法是什么?美肤、滤镜与实时处理技术讲解
  • LVS-DR+Keepalived+动静分离实验
  • ❤ Mac IDEA使用并运行项目
  • 数据清洗、特征工程和数据可视化、数据挖掘与建模的应用场景
  • tensorflow 常用代码片段
  • 网络编程值UDP
  • 解决微信小程序中 ‘nbsp;‘ 空格不生效的问题
  • pcl-3 pcl结合opencv做svm分类(法向量特征数据)
  • WPF仿网易云搭建笔记(1):项目搭建
  • 【347天】每日项目总结系列085(2018.01.18)
  • 230. Kth Smallest Element in a BST
  • exif信息对照
  • HTTP 简介
  • JavaScript工作原理(五):深入了解WebSockets,HTTP/2和SSE,以及如何选择
  • Java方法详解
  • miaov-React 最佳入门
  • MySQL主从复制读写分离及奇怪的问题
  • PHP那些事儿
  • Zsh 开发指南(第十四篇 文件读写)
  • 基于Javascript, Springboot的管理系统报表查询页面代码设计
  • 经典排序算法及其 Java 实现
  • 日剧·日综资源集合(建议收藏)
  • 算法-插入排序
  • LIGO、Virgo第三轮探测告捷,同时探测到一对黑洞合并产生的引力波事件 ...
  • 从如何停掉 Promise 链说起
  • 整理一些计算机基础知识!
  • #Linux(帮助手册)
  • $(selector).each()和$.each()的区别
  • (07)Hive——窗口函数详解
  • (2)STL算法之元素计数
  • (26)4.7 字符函数和字符串函数
  • (echarts)echarts使用时重新加载数据之前的数据存留在图上的问题
  • (iPhone/iPad开发)在UIWebView中自定义菜单栏
  • (ros//EnvironmentVariables)ros环境变量
  • (附源码)spring boot基于Java的电影院售票与管理系统毕业设计 011449
  • (附源码)ssm基于web技术的医务志愿者管理系统 毕业设计 100910
  • (附源码)小程序儿童艺术培训机构教育管理小程序 毕业设计 201740
  • (牛客腾讯思维编程题)编码编码分组打印下标(java 版本+ C版本)
  • (一)RocketMQ初步认识
  • (一)u-boot-nand.bin的下载
  • .net 打包工具_pyinstaller打包的exe太大?你需要站在巨人的肩膀上-VC++才是王道
  • .NET 命令行参数包含应用程序路径吗?
  • @FeignClient注解,fallback和fallbackFactory
  • [3D基础]理解计算机3D图形学中的坐标系变换
  • [Android实例] 保持屏幕长亮的两种方法 [转]
  • [ArcPy百科]第三节: Geometry信息中的空间参考解析
  • [C# 基础知识系列]专题十六:Linq介绍
  • [C/C++] C/C++中数字与字符串之间的转换
  • [CTF]php is_numeric绕过