在C#编程中,yield关键字是构建迭代器的核心语法,配合yield return可以实现惰性求值的效果,避免一次性加载大量数据导致内存占用过高的问题。这种特性在处理大数据集、异步数据流等场景时非常实用,能够显著提升程序的运行效率。

yield迭代器的基本用法
yield关键字只能在返回IEnumerable、IEnumerable<T>、IEnumerator或IEnumerator<T>的方法中使用,方法内部不需要手动实现迭代器的MoveNext和Current逻辑,编译器会自动生成对应的状态机代码。
下面是一个简单的yield迭代器示例,生成1到指定数量的整数序列:
using System;
using System.Collections.Generic;
public class YieldDemo
{
// 生成1到count的整数序列
public static IEnumerable<int> GenerateNumbers(int count)
{
for (int i = 1; i <= count; i++)
{
Console.WriteLine($"生成数字: {i}");
yield return i;
}
}
public static void Main()
{
Console.WriteLine("开始遍历序列");
foreach (var num in GenerateNumbers(3))
{
Console.WriteLine($"获取到数字: {num}");
}
}
}
运行上述代码,输出顺序为:
开始遍历序列 生成数字: 1 获取到数字: 1 生成数字: 2 获取到数字: 2 生成数字: 3 获取到数字: 3
可以看到,每次循环到yield return时才会生成对应的元素,而不是提前生成所有元素。
yield return的惰性求值原理
惰性求值(Lazy Evaluation)指的是表达式不在定义时求值,而是在第一次被使用时才计算结果的特性。yield return正是通过这种方式工作:
- 包含yield语句的方法被调用时,不会立即执行方法体内的代码,而是返回一个迭代器对象
- 当外部代码通过foreach遍历该迭代器时,才会执行方法体内的代码,直到遇到第一个
yield return,返回当前元素并暂停执行 - 下一次遍历时,从上次暂停的位置继续执行,直到遇到下一个
yield return或者方法结束
我们可以通过对比普通集合和yield迭代器的内存占用来验证这个特性:
using System;
using System.Collections.Generic;
using System.Diagnostics;
public class MemoryCompare
{
// 普通方式生成序列,一次性加载所有数据到内存
public static List<int> GenerateNumbersNormal(int count)
{
List<int> result = new List<int>();
for (int i = 0; i < count; i++)
{
result.Add(i);
}
return result;
}
// yield方式生成序列,惰性生成数据
public static IEnumerable<int> GenerateNumbersYield(int count)
{
for (int i = 0; i < count; i++)
{
yield return i;
}
}
public static void Main()
{
int count = 10000000;
// 测试普通方式内存占用
var normalList = GenerateNumbersNormal(count);
Console.WriteLine($"普通集合元素数量: {normalList.Count}");
// 测试yield方式内存占用
var yieldIter = GenerateNumbersYield(count);
int iterCount = 0;
foreach (var num in yieldIter)
{
iterCount++;
if (iterCount == 1) break; // 只取第一个元素就停止
}
Console.WriteLine($"yield迭代器遍历1个元素后的计数: {iterCount}");
}
}
运行后可以发现,普通方式会一次性生成1000万个整数存入List,占用大量内存;而yield方式只遍历了1个元素,后续元素根本没有被生成,内存占用极低。
yield迭代器的进阶使用场景
1. 处理大文件读取
读取超大文本文件时,不需要一次性把文件所有内容加载到内存,可以用yield逐行返回:
using System;
using System.Collections.Generic;
using System.IO;
public class FileReadDemo
{
public static IEnumerable<string> ReadFileByLine(string filePath)
{
using (StreamReader reader = new StreamReader(filePath))
{
string line;
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
}
public static void Main()
{
string path = "large_file.txt";
foreach (var line in ReadFileByLine(path))
{
// 逐行处理,不需要加载整个文件到内存
if (line.Contains("目标关键词"))
{
Console.WriteLine(line);
}
}
}
}
2. 无限序列生成
yield可以很方便地生成无限序列,因为不需要提前生成所有元素:
using System;
using System.Collections.Generic;
public class InfiniteSequence
{
// 生成无限递增的整数序列
public static IEnumerable<int> GenerateInfiniteNumbers()
{
int current = 0;
while (true)
{
yield return current;
current++;
}
}
public static void Main()
{
int sum = 0;
foreach (var num in GenerateInfiniteNumbers())
{
sum += num;
if (sum > 100) break; // 累加和超过100就停止遍历
Console.WriteLine($"当前数字: {num}, 累加和: {sum}");
}
}
}
3. 数据过滤与转换管道
可以组合多个yield迭代器实现数据处理管道,每个步骤都保持惰性:
using System;
using System.Collections.Generic;
public class DataPipeline
{
// 生成原始数据
public static IEnumerable<int> GenerateData()
{
for (int i = 0; i < 20; i++)
{
yield return i;
}
}
// 过滤偶数
public static IEnumerable<int> FilterEven(IEnumerable<int> source)
{
foreach (var num in source)
{
if (num % 2 == 0)
{
yield return num;
}
}
}
// 将数字乘以2
public static IEnumerable<int> MultiplyByTwo(IEnumerable<int> source)
{
foreach (var num in source)
{
yield return num * 2;
}
}
public static void Main()
{
var result = MultiplyByTwo(FilterEven(GenerateData()));
foreach (var num in result)
{
Console.WriteLine(num);
}
}
}
使用yield迭代器的注意事项
- yield语句不能出现在try-catch块中,但可以出现在try-finally块的try部分
- 包含yield的方法不能包含return语句,只能使用yield return或yield break
- yield break用于提前终止迭代,类似循环中的break
- 每次调用包含yield的方法都会返回一个新的迭代器实例,迭代器之间状态相互独立
- 迭代器只能向前遍历,不支持倒序或者随机访问
合理运用yield迭代器和惰性求值特性,可以在处理大数据场景时有效降低内存占用,提升程序的响应速度,是C#进阶开发中需要掌握的重要技巧。
C#yield迭代器yield_return惰性求值迭代器模式修改时间:2026-07-06 06:00:14