C# yeild的使用

yield 是 C# 中一个非常巧妙的语法糖,它的核心作用是实现"延迟执行(Lazy Evaluation)"

简单来说,它允许你在一个方法中逐个返回数据,而不需要一次性把所有数据都计算好并塞进一个 List 里返回。

🎬 核心概念:从"批发"到"零售"

为了理解 yield,我们先看一个不使用 yield 的传统写法:

csharp 复制代码
// 传统写法:一次性生成所有数据(批发)
public List<int> GetNumbers(int count)
{
    var list = new List<int>();
    for (int i = 0; i < count; i++)
    {
        list.Add(i); // 把所有数字都塞进内存里的集合
    }
    return list; // 最后一次性返回整个集合
}

缺点 :如果 count 是 100 万,这个方法会瞬间在内存中分配一个包含 100 万个元素的数组,造成极大的内存浪费。

现在看看使用 yield 的写法:

csharp 复制代码
// yield 写法:按需生成数据(零售)
public IEnumerable<int> GetNumbersYield(int count)
{
    for (int i = 0; i < count; i++)
    {
        yield return i; // 每次吐出一个数字,然后"暂停"在这里
    }
}

优点 :调用这个方法时,它根本不会立刻执行循环 。只有当外部真正去读取下一个数据时,它才会执行一次循环,吐出一个值,然后暂停。内存中永远只存在当前这一个数字。

🛠️ 两个核心关键字

  1. yield return:返回当前值,并记住当前的执行状态(包括局部变量、循环索引等)。下次调用时,直接从这行代码的下一行继续执行。
  2. yield break:提前结束迭代,相当于普通方法里的 return

💡 典型应用场景

1. 读取超大文件或数据流

假设你要读取一个 10GB 的日志文件,如果用 File.ReadAllLines(),内存直接撑爆。用 yield 可以逐行处理:

csharp 复制代码
public IEnumerable<string> ReadLargeFile(string filePath)
{
    using (var reader = new StreamReader(filePath))
    {
        string line;
        while ((line = reader.ReadLine()) != null)
        {
            yield return line; // 读一行,吐出一行,内存占用极小
        }
    }
}

// 使用时:
foreach (var line in ReadLargeFile("huge_log.txt"))
{
    if (line.Contains("ERROR")) Console.WriteLine(line);
}
2. 实现自定义迭代器

如果你想让你自己写的类支持 foreach 循环,就可以用 yield

csharp 复制代码
public class Countdown
{
    private int _start;
    public Countdown(int start) { _start = start; }

    public IEnumerator<int> GetEnumerator()
    {
        for (int i = _start; i >= 0; i--)
        {
            yield return i;
        }
    }
}

// 使用时:
var countdown = new Countdown(5);
foreach (var num in countdown)
{
    Console.WriteLine(num); // 输出 5, 4, 3, 2, 1, 0
}
3. 提前终止(yield break)
csharp 复制代码
public IEnumerable<int> GetEvenNumbers(int max)
{
    for (int i = 0; i <= max; i++)
    {
        if (i > 10) yield break; // 如果超过10,直接结束整个迭代
        
        if (i % 2 == 0) yield return i;
    }
}

⚠️ 必须注意的"坑"

  1. 只能返回特定类型 :包含 yield 的方法,返回类型只能是 IEnumerableIEnumerable<T>IEnumeratorIEnumerator<T>。不能直接返回 List<T> 或数组。

  2. 延迟执行的副作用

    csharp 复制代码
    var query = GetNumbersYield(100); // 此时循环一次都没执行!
    
    // 只有走到这里,循环才开始
    foreach(var item in query) { ... } 

    如果你在 GetNumbersYield 里面有参数校验(比如 if(count < 0) throw...),在调用方法时不会报错 ,只有在 foreach 第一次迭代时才会报错。

  3. 不要在 yield 方法里使用 try-catch-finally 包裹 yield return :C# 编译器不允许在包含 yield returntry 块中使用 catch(但可以使用 finally)。

📌 总结

yield 的本质是状态机 。C# 编译器在底层帮你把 yield 方法编译成了一个复杂的类,自动帮你保存了局部变量和执行位置。

一句话口诀 :当数据量很大,或者数据是按需生成的,且你只需要用 foreach 去遍历它时,果断使用 yield return


相关推荐
2401_894915531 小时前
Geo 优化源码部署常见报错排查:连接失败、地图加载、地域词失效解决方案
服务器·开发语言·python·安全·php
c238561 小时前
C/C++每日一练20
c语言·开发语言·c++
可爱系程序猿1 小时前
msvcp140_codecvt_ids.dll 缺失的排查记录:C++ 程序启动异常时如何核对运行库版本
开发语言·c++·程序人生·电脑
weixin_440784112 小时前
【IntentSeivice实现原理】
android·java·开发语言·intentservice
在世修行2 小时前
从零打造 C# 工业视觉检测系统(九):工厂模式实现 SQL Server / MySQL / SQLite 三库切换
mysql·sqlite·c#·工厂模式
岁岁养乐多2 小时前
Java 序列化相关问题
java·开发语言
CAE二次开发工程师2 小时前
【C语言入门到精通】-基础篇
c语言·开发语言·python
AAIshangyanxiu3 小时前
涡度通量碳观测数据 MATLAB 全流程解析与敏感性定量分析、观测数据质控、插补及 footprint 建模技术
开发语言·matlab·涡度通量·生态碳汇·生态碳汇模拟
小短腿乄3 小时前
java实现pdf加水印+签名
java·开发语言·pdf
聆风吟º3 小时前
【金仓数据库征文】Java 应用接入金仓数据库:从驱动、连接池到存储过程调试的踩坑实录
java·开发语言·数据库