关于C#扩展方法执行顺序及yield内部工作机制的疑问
C#扩展方法与yield延迟执行机制解析
示例代码
主逻辑代码
Product[] productArray = { new Product { Name="Kayak",Price=275M}, new Product { Name="Lifejacket",Price=375M}, new Product { Name="SoccerBall",Price=475M}, new Product { Name="CornerBall",Price=775M} }; decimal arrayTotal = productArray.FilterByPrice(700).TotalPrices();
扩展方法实现
public static decimal TotalPrices(this IEnumerable<Product?> products) { decimal total = 0; foreach (Product? prod in products) { total += prod?.Price ?? 0; } return total; } public static IEnumerable<Product?> FilterByPrice(this IEnumerable<Product?> productEnum, decimal minimumPrice) { foreach(Product? prod in productEnum) { if ((prod?.Price ?? 0) >= minimumPrice) { yield return prod; } } }
疑问解答
1. 为何调试时看似先执行TotalPrices而非FilterByPrice?
这是对执行顺序的误解——编译器并没有优先执行TotalPrices,而是FilterByPrice因使用yield return实现了延迟执行。调用FilterByPrice(700)时,并不会立即执行方法体内的循环逻辑,而是由C#编译器生成一个实现IEnumerable<Product?>的状态机对象并返回,此时方法体代码完全未运行。而TotalPrices是普通的立即执行方法,调用它时会立刻进入方法体执行,直到遍历传入的枚举器时才会触发FilterByPrice的逻辑。
2. 调试时流程跳转的内部运行机制
这是yield return的核心特性——状态机执行模式:
- 当调用带
yield return的方法(如FilterByPrice)时,编译器会自动生成一个包含状态字段的类,该类实现IEnumerator<T>接口,用来跟踪方法的执行位置、局部变量等状态。此时方法体不会执行,仅返回这个状态机实例。 - 当TotalPrices中的
foreach循环开始遍历这个枚举器时,会调用枚举器的MoveNext()方法,这才会触发FilterByPrice的方法体执行:从方法开头开始,直到遇到yield return语句,此时暂停执行,将当前元素返回给TotalPrices的foreach循环,完成价格累加。 - 当TotalPrices的foreach需要下一个元素时,再次调用
MoveNext(),FilterByPrice会从上次暂停的位置继续执行,遍历下一个产品并判断价格条件,符合则再次yield返回,直到遍历完所有元素,MoveNext()返回false,循环结束。
这种跳转是状态机在MoveNext()调用时的正常执行流程,目的是实现按需迭代,避免提前遍历所有元素造成性能浪费。
内容的提问来源于stack exchange,提问作者stackoverflowresearchk879
相关产品推荐
相关产品推荐

