public static class BatchLinq {
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size) {
if (size <= 0)
throw new ArgumentOutOfRangeException("size", "Must be greater than zero.");
using (IEnumerator<T> enumerator = source.GetEnumerator())
while (enumerator.MoveNext())
yield return TakeIEnumerator(enumerator, size);
}
private static IEnumerable<T> TakeIEnumerator<T>(IEnumerator<T> source, int size) {
int i = 0;
do
yield return source.Current;
while (++i < size && source.MoveNext());
}
}
public static class BatchLinq
{
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size)
{
if (size <= 0)
throw new ArgumentOutOfRangeException("size", "Must be greater than zero.");
using (var enumerator = source.GetEnumerator())
while (enumerator.MoveNext())
{
int i = 0;
// Batch is a local function closing over `i` and `enumerator` that
// executes the inner batch enumeration
IEnumerable<T> Batch()
{
do yield return enumerator.Current;
while (++i < size && enumerator.MoveNext());
}
yield return Batch();
while (++i < size && enumerator.MoveNext()); // discard skipped items
}
}
}
[Fact]
public void ShouldPartition()
{
var ints = new List<int> {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
var data = ints.PartitionByMaxGroupSize(3);
data.Count().Should().Be(4);
data.Skip(0).First().Count().Should().Be(3);
data.Skip(0).First().ToList()[0].Should().Be(0);
data.Skip(0).First().ToList()[1].Should().Be(1);
data.Skip(0).First().ToList()[2].Should().Be(2);
data.Skip(1).First().Count().Should().Be(3);
data.Skip(1).First().ToList()[0].Should().Be(3);
data.Skip(1).First().ToList()[1].Should().Be(4);
data.Skip(1).First().ToList()[2].Should().Be(5);
data.Skip(2).First().Count().Should().Be(3);
data.Skip(2).First().ToList()[0].Should().Be(6);
data.Skip(2).First().ToList()[1].Should().Be(7);
data.Skip(2).First().ToList()[2].Should().Be(8);
data.Skip(3).First().Count().Should().Be(1);
data.Skip(3).First().ToList()[0].Should().Be(9);
}
分区数据的扩展方法。
/// <summary>
/// A set of extension methods for <see cref="IEnumerable{T}"/>.
/// </summary>
public static class EnumerableExtender
{
/// <summary>
/// Splits an enumerable into chucks, by a maximum group size.
/// </summary>
/// <param name="source">The source to split</param>
/// <param name="maxSize">The maximum number of items per group.</param>
/// <typeparam name="T">The type of item to split</typeparam>
/// <returns>A list of lists of the original items.</returns>
public static IEnumerable<IEnumerable<T>> PartitionByMaxGroupSize<T>(this IEnumerable<T> source, int maxSize)
{
return new SplittingEnumerable<T>(source, maxSize);
}
}
这是实现类
using System.Collections;
using System.Collections.Generic;
internal class SplittingEnumerable<T> : IEnumerable<IEnumerable<T>>
{
private readonly IEnumerable<T> backing;
private readonly int maxSize;
private bool hasCurrent;
private T lastItem;
public SplittingEnumerable(IEnumerable<T> backing, int maxSize)
{
this.backing = backing;
this.maxSize = maxSize;
}
public IEnumerator<IEnumerable<T>> GetEnumerator()
{
return new Enumerator(this, this.backing.GetEnumerator());
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
private class Enumerator : IEnumerator<IEnumerable<T>>
{
private readonly SplittingEnumerable<T> parent;
private readonly IEnumerator<T> backingEnumerator;
private NextEnumerable current;
public Enumerator(SplittingEnumerable<T> parent, IEnumerator<T> backingEnumerator)
{
this.parent = parent;
this.backingEnumerator = backingEnumerator;
this.parent.hasCurrent = this.backingEnumerator.MoveNext();
if (this.parent.hasCurrent)
{
this.parent.lastItem = this.backingEnumerator.Current;
}
}
public bool MoveNext()
{
if (this.current == null)
{
this.current = new NextEnumerable(this.parent, this.backingEnumerator);
return true;
}
else
{
if (!this.current.IsComplete)
{
using (var enumerator = this.current.GetEnumerator())
{
while (enumerator.MoveNext())
{
}
}
}
}
if (!this.parent.hasCurrent)
{
return false;
}
this.current = new NextEnumerable(this.parent, this.backingEnumerator);
return true;
}
public void Reset()
{
throw new System.NotImplementedException();
}
public IEnumerable<T> Current
{
get { return this.current; }
}
object IEnumerator.Current
{
get { return this.Current; }
}
public void Dispose()
{
}
}
private class NextEnumerable : IEnumerable<T>
{
private readonly SplittingEnumerable<T> splitter;
private readonly IEnumerator<T> backingEnumerator;
private int currentSize;
public NextEnumerable(SplittingEnumerable<T> splitter, IEnumerator<T> backingEnumerator)
{
this.splitter = splitter;
this.backingEnumerator = backingEnumerator;
}
public bool IsComplete { get; private set; }
public IEnumerator<T> GetEnumerator()
{
return new NextEnumerator(this.splitter, this, this.backingEnumerator);
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
private class NextEnumerator : IEnumerator<T>
{
private readonly SplittingEnumerable<T> splitter;
private readonly NextEnumerable parent;
private readonly IEnumerator<T> enumerator;
private T currentItem;
public NextEnumerator(SplittingEnumerable<T> splitter, NextEnumerable parent, IEnumerator<T> enumerator)
{
this.splitter = splitter;
this.parent = parent;
this.enumerator = enumerator;
}
public bool MoveNext()
{
this.parent.currentSize += 1;
this.currentItem = this.splitter.lastItem;
var hasCcurent = this.splitter.hasCurrent;
this.parent.IsComplete = this.parent.currentSize > this.splitter.maxSize;
if (this.parent.IsComplete)
{
return false;
}
if (hasCcurent)
{
var result = this.enumerator.MoveNext();
this.splitter.lastItem = this.enumerator.Current;
this.splitter.hasCurrent = result;
}
return hasCcurent;
}
public void Reset()
{
throw new System.NotImplementedException();
}
public T Current
{
get { return this.currentItem; }
}
object IEnumerator.Current
{
get { return this.Current; }
}
public void Dispose()
{
}
}
}
}
List<int> source = Enumerable.Range(1,23).ToList();
int batchsize = 10;
for (int i = 0; i < source.Count; i+= batchsize)
{
var batch = source.Skip(i).Take(batchsize);
}
在循环中使用 Skip 和 Take 意味着枚举数将被枚举多次。如果延迟枚举,这是危险的。它可能导致数据库查询、 Web 请求或文件读取的多次执行。这个示例显式地用于 List,因为 List 不会被延迟,所以问题不大。
这仍然是一个缓慢的解决方案,因为每次调用时跳过都会枚举集合。
这也可以用 GetRange方法解决,但是需要额外的计算来提取一个可能的休息批:
for (int i = 0; i < source.Count; i += batchsize)
{
int remaining = source.Count - i;
var batch = remaining > batchsize ? source.GetRange(i, batchsize) : source.GetRange(i, remaining);
}
这里有第三种方法来处理这个问题,它适用于2个循环。这样可以确保集合只枚举1次!:
int batchsize = 10;
List<int> batch = new List<int>(batchsize);
for (int i = 0; i < source.Count; i += batchsize)
{
// calculated the remaining items to avoid an OutOfRangeException
batchsize = source.Count - i > batchsize ? batchsize : source.Count - i;
for (int j = i; j < i + batchsize; j++)
{
batch.Add(source[j]);
}
batch.Clear();
}
下面是对 Nick Whaley 的(链接)和 infogulch 的(链接)惰性 Batch实现的一个尝试性改进。这个很严格。您要么按正确的顺序枚举批处理,要么得到一个异常。
public static IEnumerable<IEnumerable<TSource>> Batch<TSource>(
this IEnumerable<TSource> source, int size)
{
if (size <= 0) throw new ArgumentOutOfRangeException(nameof(size));
using (var enumerator = source.GetEnumerator())
{
int i = 0;
while (enumerator.MoveNext())
{
if (i % size != 0) throw new InvalidOperationException(
"The enumeration is out of order.");
i++;
yield return GetBatch();
}
IEnumerable<TSource> GetBatch()
{
while (true)
{
yield return enumerator.Current;
if (i % size == 0 || !enumerator.MoveNext()) break;
i++;
}
}
}
}
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int count)
{
if (source == null) throw new System.ArgumentNullException("source");
if (count <= 0) throw new System.ArgumentOutOfRangeException("count");
using (var enumerator = source.GetEnumerator())
{
IEnumerable<T> BatchInner()
{
int counter = 0;
do
yield return enumerator.Current;
while (++counter < count && enumerator.MoveNext());
}
while (enumerator.MoveNext())
yield return BatchInner().ToArray();
}
}
public static class Extensions
{
public static IEnumerable<TOut> Batch<T, TOut>(this IEnumerable<T> source, Func<T, T, TOut> func)
{
using (var enumerator = source.GetEnumerator())
{
while (true)
{
bool state;
state = enumerator.MoveNext(); if (!state) break; var v0 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v1 = enumerator.Current;
yield return func(v0, v1);
}
}
}
public static IEnumerable<TOut> Batch<T, TOut>(this IEnumerable<T> source, Func<T, T, T, TOut> func)
{
using (var enumerator = source.GetEnumerator())
{
while (true)
{
bool state;
state = enumerator.MoveNext(); if (!state) break; var v0 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v1 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v2 = enumerator.Current;
yield return func(v0, v1, v2);
}
}
}
public static IEnumerable<TOut> Batch<T, TOut>(this IEnumerable<T> source, Func<T, T, T, T, TOut> func)
{
using (var enumerator = source.GetEnumerator())
{
while (true)
{
bool state;
state = enumerator.MoveNext(); if (!state) break; var v0 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v1 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v2 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v3 = enumerator.Current;
yield return func(v0, v1, v2, v3);
}
}
}
public static IEnumerable<TOut> Batch<T, TOut>(this IEnumerable<T> source, Func<T, T, T, T, T, TOut> func)
{
using (var enumerator = source.GetEnumerator())
{
while (true)
{
bool state;
state = enumerator.MoveNext(); if (!state) break; var v0 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v1 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v2 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v3 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v4 = enumerator.Current;
yield return func(v0, v1, v2, v3, v4);
}
}
}
public static IEnumerable<TOut> Batch<T, TOut>(this IEnumerable<T> source, Func<T, T, T, T, T, T, TOut> func)
{
using (var enumerator = source.GetEnumerator())
{
while (true)
{
bool state;
state = enumerator.MoveNext(); if (!state) break; var v0 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v1 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v2 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v3 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v4 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v5 = enumerator.Current;
yield return func(v0, v1, v2, v3, v4, v5);
}
}
}
public static IEnumerable<TOut> Batch<T, TOut>(this IEnumerable<T> source, Func<T, T, T, T, T, T, T, TOut> func)
{
using (var enumerator = source.GetEnumerator())
{
while (true)
{
bool state;
state = enumerator.MoveNext(); if (!state) break; var v0 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v1 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v2 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v3 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v4 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v5 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v6 = enumerator.Current;
yield return func(v0, v1, v2, v3, v4, v5, v6);
}
}
}
public static IEnumerable<TOut> Batch<T, TOut>(this IEnumerable<T> source, Func<T, T, T, T, T, T, T, T, TOut> func)
{
using (var enumerator = source.GetEnumerator())
{
while (true)
{
bool state;
state = enumerator.MoveNext(); if (!state) break; var v0 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v1 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v2 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v3 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v4 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v5 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v6 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v7 = enumerator.Current;
yield return func(v0, v1, v2, v3, v4, v5, v6, v7);
}
}
}
public static IEnumerable<TOut> Batch<T, TOut>(this IEnumerable<T> source, Func<T, T, T, T, T, T, T, T, T, TOut> func)
{
using (var enumerator = source.GetEnumerator())
{
while (true)
{
bool state;
state = enumerator.MoveNext(); if (!state) break; var v0 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v1 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v2 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v3 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v4 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v5 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v6 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v7 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v8 = enumerator.Current;
yield return func(v0, v1, v2, v3, v4, v5, v6, v7, v8);
}
}
}
public static IEnumerable<TOut> Batch<T, TOut>(this IEnumerable<T> source, Func<T, T, T, T, T, T, T, T, T, T, TOut> func)
{
using (var enumerator = source.GetEnumerator())
{
while (true)
{
bool state;
state = enumerator.MoveNext(); if (!state) break; var v0 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v1 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v2 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v3 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v4 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v5 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v6 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v7 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v8 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v9 = enumerator.Current;
yield return func(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
}
}
}
public static IEnumerable<TOut> Batch<T, TOut>(this IEnumerable<T> source, Func<T, T, T, T, T, T, T, T, T, T, T, TOut> func)
{
using (var enumerator = source.GetEnumerator())
{
while (true)
{
bool state;
state = enumerator.MoveNext(); if (!state) break; var v0 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v1 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v2 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v3 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v4 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v5 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v6 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v7 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v8 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v9 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v10 = enumerator.Current;
yield return func(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10);
}
}
}
public static IEnumerable<TOut> Batch<T, TOut>(this IEnumerable<T> source, Func<T, T, T, T, T, T, T, T, T, T, T, T, TOut> func)
{
using (var enumerator = source.GetEnumerator())
{
while (true)
{
bool state;
state = enumerator.MoveNext(); if (!state) break; var v0 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v1 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v2 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v3 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v4 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v5 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v6 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v7 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v8 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v9 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v10 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v11 = enumerator.Current;
yield return func(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11);
}
}
}
public static IEnumerable<TOut> Batch<T, TOut>(this IEnumerable<T> source, Func<T, T, T, T, T, T, T, T, T, T, T, T, T, TOut> func)
{
using (var enumerator = source.GetEnumerator())
{
while (true)
{
bool state;
state = enumerator.MoveNext(); if (!state) break; var v0 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v1 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v2 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v3 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v4 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v5 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v6 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v7 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v8 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v9 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v10 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v11 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v12 = enumerator.Current;
yield return func(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12);
}
}
}
public static IEnumerable<TOut> Batch<T, TOut>(this IEnumerable<T> source, Func<T, T, T, T, T, T, T, T, T, T, T, T, T, T, TOut> func)
{
using (var enumerator = source.GetEnumerator())
{
while (true)
{
bool state;
state = enumerator.MoveNext(); if (!state) break; var v0 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v1 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v2 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v3 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v4 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v5 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v6 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v7 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v8 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v9 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v10 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v11 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v12 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v13 = enumerator.Current;
yield return func(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13);
}
}
}
public static IEnumerable<TOut> Batch<T, TOut>(this IEnumerable<T> source, Func<T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, TOut> func)
{
using (var enumerator = source.GetEnumerator())
{
while (true)
{
bool state;
state = enumerator.MoveNext(); if (!state) break; var v0 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v1 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v2 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v3 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v4 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v5 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v6 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v7 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v8 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v9 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v10 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v11 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v12 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v13 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v14 = enumerator.Current;
yield return func(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14);
}
}
}
public static IEnumerable<TOut> Batch<T, TOut>(this IEnumerable<T> source, Func<T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, TOut> func)
{
using (var enumerator = source.GetEnumerator())
{
while (true)
{
bool state;
state = enumerator.MoveNext(); if (!state) break; var v0 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v1 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v2 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v3 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v4 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v5 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v6 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v7 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v8 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v9 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v10 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v11 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v12 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v13 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v14 = enumerator.Current;
state = enumerator.MoveNext(); if (!state) break; var v15 = enumerator.Current;
yield return func(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15);
}
}
}
}
下面是一个用法示例:
using System;
using System.Linq;
namespace TestProgram
{
class Program
{
static void Main(string[] args)
{
foreach (var item in Enumerable.Range(0, 12).ToArray().Batch((R, X1, Y1, X2, Y2) => (R, X1, Y1, X2, Y2)))
{
Console.WriteLine($"{item.R}, {item.X1}, {item.Y1}, {item.X2}, {item.Y2}");
}
}
}
}
public static class EnumerableExtensions
{
/// <summary>
/// Chunks a sequence into a sub-sequences each containing maxItemsPerChunk, except for the last
/// which will contain any items left over.
///
/// NOTE: this implements a streaming implementation via <seealso cref="IAsyncEnumerable{T}"/>.
/// </summary>
public static async IAsyncEnumerable<IEnumerable<T>> ChunkAsync<T>(this IAsyncEnumerable<T> sequence, int maxItemsPerChunk)
{
if (sequence == null) throw new ArgumentNullException(nameof(sequence));
if (maxItemsPerChunk <= 0)
{
throw new ArgumentOutOfRangeException(nameof(maxItemsPerChunk), $"{nameof(maxItemsPerChunk)} must be greater than 0");
}
var chunk = new List<T>(maxItemsPerChunk);
await foreach (var item in sequence)
{
chunk.Add(item);
if (chunk.Count == maxItemsPerChunk)
{
yield return chunk.ToArray();
chunk.Clear();
}
}
// return the "crumbs" that
// didn't make it into a full chunk
if (chunk.Count > 0)
{
yield return chunk.ToArray();
}
}
/// <summary>
/// Chunks a sequence into a sub-sequences each containing maxItemsPerChunk, except for the last
/// which will contain any items left over.
/// </summary>
public static IEnumerable<IEnumerable<T>> Chunk<T>(this IEnumerable<T> sequence, int maxItemsPerChunk)
{
if (sequence == null) throw new ArgumentNullException(nameof(sequence));
if (maxItemsPerChunk <= 0)
{
throw new ArgumentOutOfRangeException(nameof(maxItemsPerChunk), $"{nameof(maxItemsPerChunk)} must be greater than 0");
}
var chunk = new List<T>(maxItemsPerChunk);
foreach (var item in sequence)
{
chunk.Add(item);
if (chunk.Count == maxItemsPerChunk)
{
yield return chunk.ToArray();
chunk.Clear();
}
}
// return the "crumbs" that
// didn't make it into a full chunk
if (chunk.Count > 0)
{
yield return chunk.ToArray();
}
}
}