0

0

c# yield return 是什么原理

幻夢星雲

幻夢星雲

发布时间:2026-03-01 10:26:03

|

490人浏览过

|

来源于php中文网

原创

yield return 不是语法糖,而是编译器将迭代器方法重写为带状态字段和movenext()跳转逻辑的ienumerator实现类,支持懒加载与状态保持。

c# yield return 是什么原理

yield return 不是语法糖,是编译器生成状态机

它不是简单地“把 return 换成 yield return 就能懒加载”,而是 C# 编译器在编译时,把你写的迭代器方法整个重写成一个隐藏的 IEnumerator<t></t> 实现类——这个类带字段存循环变量、索引、状态码(比如 0=未开始、1=运行中、2=已结束),再配合 MoveNext() 的 switch-case 跳转逻辑。你每写一句 yield return x;,编译器就在状态机里插一个“保存当前值 → 更新状态 → 返回 true” 的分支。

  • 你无法在 try/catch 块里用 yield return(编译报错 CS1626)
  • 也不能在带 refinout 参数的方法里用(CS1627)
  • 方法返回类型必须是 IEnumerable<t></t>IAsyncEnumerable<t></t>IEnumerator<t></t>,不能是 List<t></t>T[]

为什么 foreach 一调就“动一下”,而不是全跑完?

因为调用 GetNumbers() 这类方法时,编译器生成的代码根本**不执行方法体**,只返回一个尚未启动的状态机对象。真正触发执行的是第一次调用 MoveNext()——这通常由 foreach 隐式完成。之后每次 MoveNext(),状态机才从上次 yield return 后的位置继续跑,直到下一个 yield return 或方法自然退出。

public static IEnumerable<int> GetNumbers()
{
    Console.WriteLine("Start");
    yield return 1;
    Console.WriteLine("After 1");
    yield return 2;
    Console.WriteLine("After 2");
}

上面这段代码,执行 var iter = GetNumbers(); 时,“Start”不会打印;只有 iter.GetEnumerator().MoveNext() 才会输出 “Start”,再调一次才输出 “After 1”,以此类推。

yield break 和 return 的行为差异很关键

yield break 不是“提前 return”,而是让状态机立刻跳到“已完成”状态(设 state = -1),后续所有 MoveNext() 都返回 false。而普通 return 在迭代器方法里是非法的(CS1628)——你只能用 yield break 终止迭代。

  • 想在满足条件时停止生成?用 yield break,别写 return
  • 想跳过某些项但继续?直接 continue,不用 yield
  • 异步迭代要用 async IAsyncEnumerable<t></t> + await yield return,此时底层是 AsyncIteratorMethodBuilder,不是普通状态机

容易被忽略的内存与调试陷阱

状态机会捕获方法内所有局部变量(闭包语义),哪怕只是个 int i,也会被提升为字段存在迭代器实例里。这意味着:如果迭代器长期存活(比如被缓存、传给 LINQ 方法没消费完),它持有的变量和引用都不会被 GC —— 特别是当你 yield 一个大对象或数据库连接时,极易引发内存泄漏。

  • 调试时看不到“断点停在 yield return 行”的效果,因为实际执行的是编译器生成的 MoveNext 方法
  • 不要在 yield 方法里做昂贵初始化(如打开文件、查 DB),除非确定每次迭代都需要;否则应把初始化提到外面
  • 若需复用逻辑,优先提取纯函数,而非把 yield 方法当工具链嵌套调用

真实项目里,最常出问题的地方不是“怎么写”,而是“什么时候不该用”——比如本该一次性加载的小集合,硬套 yield 反而增加状态机开销;或者在 ASP.NET Core API 中直接返回 IEnumerable<t></t> 导致序列化器多次枚举,触发重复查询。这些细节,比理解原理更影响上线结果。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
switch语句用法
switch语句用法

switch语句用法:1、Switch语句只能用于整数类型,枚举类型和String类型,不能用于浮点数类型和布尔类型;2、每个case语句后面必须跟着一个break语句,以防止执行其他case的代码块,没有break语句,将会继续执行下一个case的代码块;3、可以在一个case语句中匹配多个值,使用逗号分隔;4、Switch语句中的default代码块是可选的等等。

564

2023.09.21

Java switch的用法
Java switch的用法

Java中的switch语句用于根据不同的条件执行不同的代码块。想了解更多switch的相关内容,可以阅读本专题下面的文章。

436

2024.03.13

php中foreach用法
php中foreach用法

本专题整合了php中foreach用法的相关介绍,阅读专题下面的文章了解更多详细教程。

202

2025.12.04

java中break的作用
java中break的作用

本专题整合了java中break的用法教程,阅读专题下面的文章了解更多详细内容。

120

2025.10.15

java break和continue
java break和continue

本专题整合了java break和continue的区别相关内容,阅读专题下面的文章了解更多详细内容。

260

2025.10.24

java break和continue
java break和continue

本专题整合了java break和continue的区别相关内容,阅读专题下面的文章了解更多详细内容。

260

2025.10.24

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

890

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

594

2024.08.29

Golang 测试体系与代码质量保障:工程级可靠性建设
Golang 测试体系与代码质量保障:工程级可靠性建设

Go语言测试体系与代码质量保障聚焦于构建工程级可靠性系统。本专题深入解析Go的测试工具链(如go test)、单元测试、集成测试及端到端测试实践,结合代码覆盖率分析、静态代码扫描(如go vet)和动态分析工具,建立全链路质量监控机制。通过自动化测试框架、持续集成(CI)流水线配置及代码审查规范,实现测试用例管理、缺陷追踪与质量门禁控制,确保代码健壮性与可维护性,为高可靠性工程系统提供质量保障。

6

2026.02.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 10.4万人学习

C 教程
C 教程

共75课时 | 5.1万人学习

C++教程
C++教程

共115课时 | 19.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号