You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 16:10:12