C# yeild的使用

📅 2026/7/19 6:33:53
C# yeild的使用
yield是 C# 中一个非常巧妙的语法糖它的核心作用是实现“延迟执行Lazy Evaluation”。简单来说它允许你在一个方法中逐个返回数据而不需要一次性把所有数据都计算好并塞进一个List里返回。 核心概念从“批发”到“零售”为了理解yield我们先看一个不使用yield的传统写法// 传统写法一次性生成所有数据批发publicListintGetNumbers(intcount){varlistnewListint();for(inti0;icount;i){list.Add(i);// 把所有数字都塞进内存里的集合}returnlist;// 最后一次性返回整个集合}缺点如果count是 100 万这个方法会瞬间在内存中分配一个包含 100 万个元素的数组造成极大的内存浪费。现在看看使用yield的写法// yield 写法按需生成数据零售publicIEnumerableintGetNumbersYield(intcount){for(inti0;icount;i){yieldreturni;// 每次吐出一个数字然后“暂停”在这里}}优点调用这个方法时它根本不会立刻执行循环。只有当外部真正去读取下一个数据时它才会执行一次循环吐出一个值然后暂停。内存中永远只存在当前这一个数字。️ 两个核心关键字yield return返回当前值并记住当前的执行状态包括局部变量、循环索引等。下次调用时直接从这行代码的下一行继续执行。yield break提前结束迭代相当于普通方法里的return。 典型应用场景1. 读取超大文件或数据流假设你要读取一个 10GB 的日志文件如果用File.ReadAllLines()内存直接撑爆。用yield可以逐行处理publicIEnumerablestringReadLargeFile(stringfilePath){using(varreadernewStreamReader(filePath)){stringline;while((linereader.ReadLine())!null){yieldreturnline;// 读一行吐出一行内存占用极小}}}// 使用时foreach(varlineinReadLargeFile(huge_log.txt)){if(line.Contains(ERROR))Console.WriteLine(line);}2. 实现自定义迭代器如果你想让你自己写的类支持foreach循环就可以用yieldpublicclassCountdown{privateint_start;publicCountdown(intstart){_startstart;}publicIEnumeratorintGetEnumerator(){for(inti_start;i0;i--){yieldreturni;}}}// 使用时varcountdownnewCountdown(5);foreach(varnumincountdown){Console.WriteLine(num);// 输出 5, 4, 3, 2, 1, 0}3. 提前终止yield breakpublicIEnumerableintGetEvenNumbers(intmax){for(inti0;imax;i){if(i10)yieldbreak;// 如果超过10直接结束整个迭代if(i%20)yieldreturni;}}⚠️ 必须注意的“坑”只能返回特定类型包含yield的方法返回类型只能是IEnumerable、IEnumerableT、IEnumerator或IEnumeratorT。不能直接返回ListT或数组。延迟执行的副作用varqueryGetNumbersYield(100);// 此时循环一次都没执行// 只有走到这里循环才开始foreach(variteminquery){...}如果你在GetNumbersYield里面有参数校验比如if(count 0) throw...在调用方法时不会报错只有在foreach第一次迭代时才会报错。不要在yield方法里使用try-catch-finally包裹yield returnC# 编译器不允许在包含yield return的try块中使用catch但可以使用finally。 总结yield的本质是状态机。C# 编译器在底层帮你把yield方法编译成了一个复杂的类自动帮你保存了局部变量和执行位置。一句话口诀当数据量很大或者数据是按需生成的且你只需要用foreach去遍历它时果断使用yield return。