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

自定义Linq Batch方法执行Count()结果异常的原因咨询

自定义Linq Batch方法Count()结果异常的原因分析

问题根源

你遇到的问题核心是延迟执行+闭包共享枚举器导致的逻辑错误,具体过程如下:

  • 当调用list.Batch(2).Count()时,Count()只会遍历外层的IEnumerable<IEnumerable<int>>,不会去枚举每个内层的子集合。
  • 外层的while (enumerator.MoveNext())循环每次执行时:
    1. 调用enumerator.MoveNext(),将枚举器指向原集合的下一个元素(第一次是0,第二次是1,以此类推)
    2. 生成一个延迟执行的内层Batch()枚举器并yield return
    3. 由于内层枚举器从未被触发,枚举器不会提前移动到后续元素
  • 这个过程会重复10次(原集合有10个元素),直到enumerator.MoveNext()返回false,因此Count()最终输出10而非预期的5。

为什么某些场景看起来正常?

如果是遍历每个子集合(比如foreach循环输出每个batch的元素),代码会表现正常:

  • 每次枚举内层Batch()时,会执行do-while循环,一次性移动枚举器获取size个元素
  • 当下一次外层enumerator.MoveNext()执行时,枚举器已经指向了下一组的第一个元素,因此外层只会生成5个子集合

但这种实现是脆弱的:只要外层操作不枚举内层集合(比如Count()、Any()等),就会触发错误逻辑。

正确的实现方式

应该在生成子集合时立即获取对应数量的元素,而不是依赖后续的延迟执行。示例代码如下:

public static class EnumerableExtensions
{
    public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size)
    {
        if (size <= 0)
            throw new ArgumentOutOfRangeException(nameof(size));
        
        using (var enumerator = source.GetEnumerator())
        {
            while (enumerator.MoveNext())
            {
                var batch = new List<T>(size);
                batch.Add(enumerator.Current);
                
                for (int i = 1; i < size && enumerator.MoveNext(); i++)
                {
                    batch.Add(enumerator.Current);
                }
                
                yield return batch;
            }
        }
    }
}

这个实现会在每次外层循环时,一次性收集size个元素到List中,确保外层枚举器的移动和元素收集同步,无论后续是调用Count()还是遍历子集合,结果都符合预期。

内容的提问来源于stack exchange,提问作者Kosmonik

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 00:05:41