1.1.2 简化迭代器 yield return的使用

yield return 是一个用于简化迭代器(Iterator)实现的关键字组合。它的核心作用是让开发者能够以更简洁的方式定义一个按需生成序列的方法(生成器方法),而无需显式实现 IEnumerableIEnumerator 接口。yield return 方法会在每次迭代时按需生成下一个值,而不是一次性生成所有结果。

核心概念

  1. 延迟执行(Lazy Evaluation)

    使用 yield return 的方法会在每次迭代时按需生成下一个元素,而不是一次性生成所有元素并存储在内存中。这对处理大数据集或无限序列非常有用。

  2. 状态机(State Machine)

    编译器会自动将 yield return 方法转换为一个状态机,记录当前执行的位置。每次调用 MoveNext()(例如在 foreach 循环中)时,代码会从上一次 yield return 的位置继续执行。

  3. 返回值类型
    yield return 方法必须返回 IEnumerable<T>IEnumerator<T> 类型。

例1:生成一个数字序列

cs 复制代码
public class Program
{
    static void Main()
    {
        // 调用生成器方法,但此时不会立即执行所有代码
        IEnumerable<int> numbers = GenerateNumbers(3);

        // 开始迭代时,按需生成值
        foreach (int num in numbers)
        {
            Console.WriteLine(num); // 依次输出 0, 1, 2
        }
    }

    // 生成器方法:按需生成 0 到 max-1 的数字
    static IEnumerable<int> GenerateNumbers(int max)
    {
        for (int i = 0; i < max; i++)
        {
            yield return i; // 每次迭代返回一个值,并暂停执行
        }
    }
}

例2:过滤集合中的偶数

cs 复制代码
public static IEnumerable<int> FilterEven(IEnumerable<int> numbers)
{
    foreach (int num in numbers)
    {
        if (num % 2 == 0)
        {
            yield return num; // 按需返回符合条件的值
        }
    }
}

// 使用:
var numbers = new List<int> { 1, 2, 3, 4, 5 };
foreach (int even in FilterEven(numbers))
{
    Console.WriteLine(even); // 输出 2, 4
}

例3:关于yield return 与普通方法的内存占用对比

下面的代码GetNumbers方法一次性把所有数据都产生,内存占用与count成线性关系。

而GenerateNumbers 调用时才产生一个数字,内存占用小。

复制代码
List<int> GetNumbers(int count)
{
    List<int> list = new List<int>();
    for (int i = 0; i < count; i++)
    {
        list.Add(i);
    }
    return list; // 内存占用:O(n)
}
复制代码
IEnumerable<int> GenerateNumbers(int count)
{
    for (int i = 0; i < count; i++)
    {
        yield return i; // 内存占用:O(1)
    }
}