yield return 是 C# 中用于定义迭代器方法的关键字,它不终止方法而是暂停执行并返回一个值,由编译器生成状态机实现 IEnumerable;必须返回 IEnumerable 等类型,不可混用普通 return,且受 try/catch 和闭包陷阱等限制。

yield return 是什么,它不是返回值而是挂起点
yield return 不是普通函数的 return,它不会终止方法执行,而是暂停当前迭代器状态,把值“交出去”,等下一次调用 MoveNext() 时从暂停处继续。这意味着方法体实际被编译成一个实现了 IEnumerable 和 IEnumerator 的状态机类,你写的代码只是语法糖。
常见误解是把它当“逐个 return”,结果在循环里写了 return 混用,导致后续 yield return 永远不执行 —— 这种写法直接报错或逻辑中断。
- 方法返回类型必须是
IEnumerable、IEnumerable、IAsyncEnumerable(C# 8+)之一 - 方法体内不能有普通
return语句(除了return;用于提前退出迭代) - 不能在
try块中有yield return,除非catch和finally中没有yield return(编译器限制)
怎么写一个基础 yield return 迭代器方法
最典型场景:把一个计算过程或数据流封装成可枚举对象,避免一次性加载全部数据到内存。
public static IEnumerableGetEvenNumbers(int max) { for (int i = 0; i <= max; i += 2) { yield return i; } }
调用时:
foreach (int n in GetEvenNumbers(10))
{
Console.WriteLine(n); // 输出 0, 2, 4, 6, 8, 10
}注意:GetEvenNumbers(10) 调用本身不执行循环,只返回一个未启动的迭代器;真正执行从 foreach 第一次调用 MoveNext() 开始。
- 每次
yield return后,方法暂停,局部变量(如i)状态被保留 - 如果想中途退出,用
yield break;,它相当于“迭代结束”,不是异常 - 不要在
yield return后写任何代码(除非是yield break;或空语句),编译器会报错
yield return 和 List.Add 的性能与内存差异
对比两种实现方式:
// ❌ 先构造完整列表再返回 public static ListGetEvenNumbersList(int max) { var list = new List (); for (int i = 0; i <= max; i += 2) { list.Add(i); } return list; }
// ✅ yield return 流式生成 public static IEnumerableGetEvenNumbers(int max) { for (int i = 0; i <= max; i += 2) { yield return i; } }
关键区别:
-
List版本必须分配足够内存容纳所有元素(比如max = 1000000就要存 50 万整数),且全部算完才返回 -
yield return版本按需计算,内存占用恒定(仅保存当前状态),适合大数据流、IO 边界(如逐行读文件)、或消费者可能提前退出的场景(如.FirstOrDefault()) - 但无法重复遍历(除非显式调用
.ToList()),因为每次调用都新建迭代器
容易踩的坑:闭包捕获和延迟执行陷阱
下面这段代码很常见,但结果不符合直觉:
public static IEnumerable> GetDelegates() { var actions = new List >(); for (int i = 0; i < 3; i++) { yield return () => i; // ❌ 所有委托都返回 3 } }
原因:所有 yield return 返回的 lambda 共享同一个变量 i,而迭代器直到遍历时才执行,此时循环早已结束,i == 3。
修复方式:在循环内创建局部副本:
for (int i = 0; i < 3; i++)
{
int localI = i; // ✅ 每次迭代独立副本
yield return () => localI;
}另一个坑是误以为 yield return 方法“立即执行”——它其实完全惰性。如果你在 yield return 方法里打开文件、数据库连接或 HTTP 请求,这些资源会在第一次 MoveNext() 时才初始化,且若没正确释放(比如没用 using 包裹),容易造成资源泄漏或并发问题。










