我正在查看
Batch
方法的源代码,我看到了这个:
- // Select is necessary so bucket contents are streamed too
- yield return resultSelector(bucket.Select(x => x));
有一条我不太明白的评论.我已经测试了这种方法而没有使用Select,它运行良好.但似乎有一些我遗漏的东西.我想不出任何必要的例子,那么在这里使用Select(x => x)的实际目的是什么?
以下是完整的源代码供参考:
- private static IEnumerable<TResult> BatchImpl<TSource,TResult>(
- this IEnumerable<TSource> source,int size,Func<IEnumerable<TSource>,TResult> resultSelector)
- {
- TSource[] bucket = null;
- var count = 0;
- foreach (var item in source)
- {
- if (bucket == null)
- bucket = new TSource[size];
- bucket[count++] = item;
- // The bucket is fully buffered before it's yielded
- if (count != size)
- continue;
- // Select is necessary so bucket contents are streamed too
- yield return resultSelector(bucket.Select(x => x));
- bucket = null;
- count = 0;
- }
- // Return the last bucket with all remaining elements
- if (bucket != null && count > 0)
- yield return resultSelector(bucket.Take(count));
- }
解决方法
总结评论中的内容,理论上这是多余的.在这种情况下,延迟执行无关紧要.在收益率已经完成执行:已经计算了桶的内容,没有什么可以推迟.
迭代器块行为也没有问题 – 每次我们回到这个实现时,都会重置并重新创建存储桶(在yield之后,bucket = null).即使有人将结果转换为数组类型并修改它,我们也不在乎.
这种方法的一个优点似乎只是优雅:在对resultSelector的所有调用之间存在类型一致性.没有“冗余”选择,实际类型大部分时间都是TSource [],IEnumerable< TSource>对于没有填满整个桶的尾随元素.
但是,可以想象以下场景:
>使用此函数的人注意到实际类型是数组
>由于需要提高性能,他们将收到的批量转换为TSource [](例如,他们现在可以更有效地跳过元素,as Skip
is not optimized for arrays)
>他们使用该方法没有任何问题,因为在他们的情况下发生Count()%size == 0
直到,之后,会发生一个额外的元素弹出,导致最后一个产量被执行.现在,对TSource []的强制转换将会失败.
因此,根据元素的数量和大小,方法在结果类型方面的行为会不一致(传递给给定的回调).可以想象其他复杂的情况,这种不一致可能会导致麻烦,例如某些ORM,根据实际类型,将对象序列化到不同的表中.在这种情况下,数据片段最终会出现在不同的表格中.
这些场景当然都是基于其他一些错误,并没有证明没有Select实现是错误的.然而,从某种意义上说,它对Select更加友好,它将这种不幸情景的数量减少到最少.