自定义Linq Batch方法执行Count()结果异常的原因咨询
自定义Linq Batch方法Count()结果异常的原因分析
问题根源
你遇到的问题核心是延迟执行+闭包共享枚举器导致的逻辑错误,具体过程如下:
- 当调用
list.Batch(2).Count()时,Count()只会遍历外层的IEnumerable<IEnumerable<int>>,不会去枚举每个内层的子集合。 - 外层的
while (enumerator.MoveNext())循环每次执行时:- 调用
enumerator.MoveNext(),将枚举器指向原集合的下一个元素(第一次是0,第二次是1,以此类推) - 生成一个延迟执行的内层
Batch()枚举器并yield return - 由于内层枚举器从未被触发,枚举器不会提前移动到后续元素
- 调用
- 这个过程会重复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
相关产品推荐
相关产品推荐

