
- [C# 中的迭代器](# 中的迭代器)
- 二、迭代器是怎么工作的
- [三、`yield return` 为什么特别重要](#三、
yield return为什么特别重要) - 四、迭代器的底层原理
- [五、`IEnumerable` 和 `IEnumerator` 的区别](#五、
IEnumerable和IEnumerator的区别) - 六、使用迭代器需要注意什么?
-
- [1. 注意延迟执行](#1. 注意延迟执行)
- 七、重复遍历可能导致代码重复执行
- [八、注意迭代器的性能和 GC](#八、注意迭代器的性能和 GC)
- 九、不要在热点逻辑中频繁创建复杂迭代器
- 十、不要在迭代过程中随意修改集合
- [十一、Unity 中还有一个特殊点:不要把 C# 迭代器和 Unity 协程完全混为一谈](# 迭代器和 Unity 协程完全混为一谈)
- 十二、面试高频追问
-
-
- [Q1:`yield return` 和 `return` 有什么区别?](#Q1:
yield return和return有什么区别?) - [Q2:`IEnumerable` 和 `IEnumerator` 有什么区别?](#Q2:
IEnumerable和IEnumerator有什么区别?) - [Q3:`yield return` 为什么可以暂停方法?](#Q3:
yield return为什么可以暂停方法?) - [Q4:`IEnumerable` 是不是数据已经算好了?](#Q4:
IEnumerable是不是数据已经算好了?) - [Q5:Unity 协程和 C# 迭代器有什么关系?](# 迭代器有什么关系?)
- [Q1:`yield return` 和 `return` 有什么区别?](#Q1:
-
- 十三、面试口述总结
C# 中的迭代器
一、核心结论
C# 中的迭代器(Iterator) ,本质上是一种按需、逐个产生数据的遍历机制。
最常见的实现方式就是:
csharp
yield return
例如:
csharp
IEnumerable<int> GetNumbers()
{
yield return 1;
yield return 2;
yield return 3;
}
然后:
csharp
foreach (var n in GetNumbers())
{
Debug.Log(n);
}
这里 GetNumbers() 并不会一次性把 1、2、3 全部计算出来,而是 foreach 每取一个元素,就继续执行到下一个 yield return。
所以面试里最重要的一句话是:
迭代器的核心是延迟执行 + 状态保存,通过
yield return可以把一个方法变成可逐步遍历的数据源。
二、迭代器是怎么工作的
看这个例子:
csharp
IEnumerable<int> GetNumbers()
{
Debug.Log("Start");
yield return 1;
Debug.Log("After 1");
yield return 2;
Debug.Log("After 2");
yield return 3;
}
调用:
csharp
var numbers = GetNumbers();
Debug.Log("A");
这时候实际上:
text
GetNumbers()
本体不会真正执行
真正开始执行是在:
csharp
foreach (var n in numbers)
或者:
csharp
var enumerator = numbers.GetEnumerator();
enumerator.MoveNext();
之后。
执行过程可以理解成:
text
MoveNext()
↓
执行到 yield return 1
↓
暂停
↓
当前状态保存下来
再次 MoveNext()
↓
从上次暂停的位置继续
↓
yield return 2
所以迭代器本质上包含两件事情:
text
数据产生
+
遍历状态保存
三、yield return 为什么特别重要
yield return 最大的作用就是:
暂停当前方法,把一个值返回给调用者,下次继续从暂停的位置执行。
例如:
csharp
IEnumerable<int> Test()
{
Debug.Log("1");
yield return 10;
Debug.Log("2");
yield return 20;
Debug.Log("3");
}
执行:
csharp
var e = Test().GetEnumerator();
e.MoveNext(); // 输出 1,返回 10
e.MoveNext(); // 输出 2,返回 20
e.MoveNext(); // 输出 3,结束
它和普通 return 最大的区别是:
text
return
→ 方法结束
yield return
→ 暂停方法
→ 返回当前值
→ 下次继续执行
四、迭代器的底层原理
这个是 Unity 面试中比较容易被追问的地方。
编译器并不会真的让原方法"暂停在某一行"。
实际上,C# 编译器会把包含 yield 的方法转换成一个状态机(State Machine)。
概念上类似:
text
Iterator State Machine
state = 0
↓
MoveNext()
↓
yield return 1
↓
state = 1
↓
return true
再次 MoveNext()
↓
根据 state = 1
↓
继续执行
↓
yield return 2
所以面试官如果问:
"yield return 是怎么实现的?"
可以回答:
编译器会把迭代器方法转换成一个状态机,通过
IEnumerator的MoveNext()推进执行,并保存当前迭代状态。
五、IEnumerable 和 IEnumerator 的区别
这个非常高频。
IEnumerable
代表:
"我可以被遍历。"
例如:
csharp
IEnumerable<int>
它主要提供:
csharp
GetEnumerator()
拿到一个迭代器。
IEnumerator
代表:
"我当前正在怎么遍历。"
核心是:
csharp
MoveNext()
Current
关系可以记成:
text
IEnumerable
↓ GetEnumerator()
IEnumerator
↓
MoveNext()
↓
Current
所以:
IEnumerable 是数据源,IEnumerator 是遍历过程。
六、使用迭代器需要注意什么?
1. 注意延迟执行
这是最重要的问题之一。
例如:
csharp
IEnumerable<int> GetNumbers()
{
Debug.Log("Execute");
yield return 1;
}
执行:
csharp
var result = GetNumbers();
并不会打印:
text
Execute
真正遍历时才执行。
所以:
csharp
var result = GetNumbers();
foreach (var item in result)
{
}
才会真正触发逻辑。
面试要点
使用
IEnumerable时要注意它可能是延迟执行的,方法调用本身不代表数据已经计算完成。
七、重复遍历可能导致代码重复执行
例如:
csharp
IEnumerable<int> GetNumbers()
{
Debug.Log("Generate");
yield return 1;
yield return 2;
}
然后:
csharp
var result = GetNumbers();
foreach (var x in result)
{
}
foreach (var x in result)
{
}
你可能以为数据已经保存了。
实际上:
text
第一次 foreach
→ 执行一次迭代器
第二次 foreach
→ 可能重新创建 Enumerator
→ 重新执行迭代器
因此:
IEnumerable 不一定代表一个已经计算好的集合。
如果需要缓存结果,可以:
csharp
var list = GetNumbers().ToList();
之后遍历 list。
八、注意迭代器的性能和 GC
在 Unity 中尤其重要。
例如:
csharp
foreach (var item in someEnumerable)
{
}
虽然 foreach 很方便,但具体是否产生 GC、是否装箱,取决于遍历对象的具体类型和实现。
尤其要注意:
text
值类型 Enumerator
+
接口 IEnumerable / IEnumerator
+
装箱
可能产生额外开销。
所以在 Unity 高频代码,比如:
text
Update
LateUpdate
战斗循环
大量对象遍历
不要简单认为:
foreach一定零 GC。
应该结合具体集合类型和 Profiler 验证。
九、不要在热点逻辑中频繁创建复杂迭代器
例如:
csharp
void Update()
{
var result = GetEnemies()
.Where(x => x.IsAlive)
.Select(x => x.Target);
}
这里不仅有迭代器,还有 LINQ、Lambda、延迟执行等机制。
如果这是每帧执行的代码,就需要警惕:
text
迭代器开销
+
Lambda
+
LINQ
+
GC
Unity 项目中常见做法是:
非热点逻辑可以追求可读性;高频性能代码则优先控制分配和遍历成本。
十、不要在迭代过程中随意修改集合
例如:
csharp
foreach (var item in list)
{
list.Remove(item);
}
很多集合会直接抛出:
text
InvalidOperationException
因为 Enumerator 通常会检测集合版本变化。
更安全的方式可以是:
csharp
for (int i = list.Count - 1; i >= 0; i--)
{
if (NeedRemove(list[i]))
{
list.RemoveAt(i);
}
}
或者:
csharp
var removeList = ...
遍历结束后统一修改。
这个属于 foreach / Enumerator 的实际使用注意点,面试时提一下比较加分。
十一、Unity 中还有一个特殊点:不要把 C# 迭代器和 Unity 协程完全混为一谈
Unity 中经常看到:
csharp
IEnumerator Start()
{
yield return new WaitForSeconds(1);
}
很多人会说:
"IEnumerator 就是协程。"
这个说法不准确。
更准确的是:
C# 的
IEnumerator是迭代机制;Unity 在此基础上使用IEnumerator+yield实现协程调度。
例如:
csharp
IEnumerator Test()
{
Debug.Log("A");
yield return new WaitForSeconds(1);
Debug.Log("B");
}
这里 yield return 本质上仍然是在产生一个迭代结果,而 Unity 的协程系统会拿到这个结果,根据 WaitForSeconds 等对象决定什么时候继续调用 MoveNext()。
可以理解成:
text
C# Iterator
↓
IEnumerator
↓
Unity 协程调度器
↓
MoveNext()
↓
继续执行
这是 Unity 面试很常见的加分点。
十二、面试高频追问
Q1:yield return 和 return 有什么区别?
return直接结束方法;yield return暂停迭代器,把当前值返回,下次通过MoveNext()从当前位置继续执行。
Q2:IEnumerable 和 IEnumerator 有什么区别?
IEnumerable表示一个可遍历的数据源,通过GetEnumerator()获取IEnumerator;IEnumerator负责具体的遍历状态,通过MoveNext()和Current获取数据。
Q3:yield return 为什么可以暂停方法?
编译器会把迭代器方法转换为状态机,通过保存状态记录当前执行位置,每次
MoveNext()时从对应状态继续执行。
Q4:IEnumerable 是不是数据已经算好了?
不一定。它可能采用延迟执行,真正遍历时才执行计算,所以重复遍历可能重复执行生成逻辑。
Q5:Unity 协程和 C# 迭代器有什么关系?
Unity 协程基于
IEnumerator和yield机制实现,但 IEnumerator 本身只是 C# 的迭代机制,并不等同于 Unity 协程。
十三、面试口述总结
C# 的迭代器本质上是一种按需产生数据、逐步遍历的机制,最典型的实现就是
yield return。
使用yield的方法会被编译器转换成一个状态机,通过IEnumerator的MoveNext()保存和推进当前执行状态。IEnumerable表示可遍历的数据源,IEnumerator负责具体的遍历过程。
使用时需要注意延迟执行、重复遍历可能导致逻辑重复执行,以及 Unity 高频代码中的 GC 和性能问题。
另外 Unity 协程本质上也是建立在 IEnumerator 和 yield 机制之上的,但 IEnumerator 本身并不等于协程。
🎬 博客主页:https://xiaoy.blog.csdn.net
🎥 本文由 呆呆敲代码的小Y 原创 🙉
🎄 学习专栏推荐:Unity系统学习专栏
🌲 游戏制作专栏推荐:游戏制作
🌲Unity实战100例专栏推荐:Unity 实战100例 教程
🏅 欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📆 未来很长,值得我们全力奔赴更美好的生活✨
------------------❤️分割线❤️-------------------------




资料白嫖,技术互助
| 学习路线指引(点击解锁) | 知识定位 | 人群定位 |
|---|---|---|
| 🧡 Unity系统学习专栏 | 入门级 | 本专栏从Unity入门开始学习,快速达到Unity的入门水平 |
| 💛 Unity实战类项目 | 进阶级 | 计划制作Unity的 100个实战案例!助你进入Unity世界,争取做最全的Unity原创博客大全。 |
| ❤️ 游戏制作专栏 | 难度偏高 | 分享学习一些Unity成品的游戏Demo和其他语言的小游戏! |
| 💚 游戏爱好者万人社区 | 互助/吹水 | 数万人游戏爱好者社区,聊天互助,白嫖奖品 |
| 💙 Unity100个实用技能 | Unity查漏补缺 | 针对一些Unity中经常用到的一些小知识和技能进行学习介绍,核心目的就是让我们能够快速学习Unity的知识以达到查漏补缺 |
