公司动态
C# 迭代器状态机:编译器如何用 MoveNext 实现协程
开场相信写过 Unity 的同学都遇到过这种场景:策划临时改了需求,要做一个"技能读条 3 秒,期间可以被打断"的效果。你麻利地写下StartCoroutine(CastSkill()),在协程里yield return new WaitForSeconds(3f),信心满满地按下 Play。结果断点调试时一脸懵——这玩意儿既不是线程,又不是回调,WaitForSeconds内部根本没有你的代码可跟。更痛苦的是,当你想中途StopCoroutine,却发现try/finally里的清理逻辑到底执不执行,心里完全没底。这一切困惑的根源,都藏在编译器生成的"隐藏代码"里。yield return不是运行时魔法,而是编译期的代码重写。今天我们就掀开这层斗篷:先看迭代器状态机长什么样,再看编译器具体怎么翻译,最后回答"为什么非得这样设计"以及工程上怎么避坑。一、先有直觉:手写一个状态机1.1 迭代器协议是什么C# 的foreach能遍历数组、List、字典,靠的是IEnumerable/IEnumerator这对接口。IEnumerator就是那枚"游标",只有三个成员:Current(当前值)、MoveNext()(前进一步,返回是否还有货)、Reset()(重置)。