Chunk 方法仅在 .NET 6+ 可用,旧版本会编译报错;它返回 IEnumerable 而非 IAsyncEnumerable,size ≤ 0 时抛 ArgumentException,且分块过程触发立即执行并产生数组分配开销。
Chunk 方法在 .NET 6+ 才可用,旧版本直接报错
如果你用的是 .net 5 或更早版本,调用 chunk 会得到编译错误:'ienumerable<t>' does not contain a definition for 'chunk'</t>。这不是写法问题,是 api 根本不存在。
解决办法只有两个:
升级到 .NET 6 或更高(推荐),或者手动实现分块逻辑。别试图通过 NuGet 安装“补丁包”——微软没发布过兼容旧框架的 Chunk 扩展。
- .NET 6+:直接用
Enumerable.Chunk,无需额外引用 - .NET 5 及以下:用
GroupBy((_, i) => i / size)或手写循环,但要注意索引溢出和空集合边界 - Unity 项目特别注意:默认用的不是完整 .NET,即使标称支持 .NET 6,
Chunk也可能被裁剪掉,得实测
Chunk 返回的是 IAsyncEnumerable?不,它返回 IEnumerable
常见误解是以为 Chunk 和 Buffer(如 System.Reactive)一样返回流式结果,其实它同步执行、立即分配内存、返回的是 IEnumerable<t></t> —— 每个元素是一个数组,不是 IEnumerable<t></t>。
这意味着:
- 调用后整个源集合会被遍历一次,但不会一次性把所有块加载进内存(惰性求值)
- 每个块是独立数组,修改某个块里的元素不会影响原集合,但块内数组可变
- 如果源是数据库查询(如 EF Core 的
IQueryable),Chunk会强制触发执行,变成内存操作 —— 别在大表上直接context.Items.AsEnumerable().Chunk(100)
示例:var chunks = list.Chunk(3); → 得到 IEnumerable<int></int>,不是 IAsyncEnumerable<int></int>,也不支持 await foreach(除非你自己包装)
size 参数为 0 或负数时抛 ArgumentException
Chunk 对参数极其严格:只要 size ,立刻抛出 <code>ArgumentException,连 null 检查都不做 —— 它只检查 size。
生产环境常见翻车点:
- 从配置读取分块大小,没校验是否为正整数,配置写成
"batchSize": 0就崩 - 计算动态 size 时出现除零或负值,比如
Math.Max(0, total / threadCount)→ 结果可能是 0 - 前端传参未校验,URL 里带
?size=-1直接炸
安全写法:int safeSize = Math.Max(1, configuredSize); var chunks = source.Chunk(safeSize);
大集合分块时内存占用比预想高
Chunk 看似“懒”,但每次迭代一个块时,它内部会预分配该块所需长度的数组,并复制元素。对超大集合(比如百万级 string),频繁分配短生命周期数组会加剧 GC 压力。
真实瓶颈往往不在算法,而在对象分配模式:
- 每块都 new 一个数组 → 大量小对象进入 Gen 0
- 如果块内元素本身是引用类型(如
Customer),只是复制引用,开销小;但如果是struct(如DateTime数组),就真拷贝数据 - 替代方案:用
Span<t></t>+ 手动切片(需 unsafe 或 ReadOnlySpan 支持),但失去 LINQ 链式调用便利性
简单验证内存行为:GC.CollectionCount(0) 在循环 chunk 前后对比,能明显看到 Gen 0 次数飙升











