using System.Collections.Generic;
using System.Threading.Tasks;
public static class Program
public static void Main()
static IEnumerable<int> Produce()
Console.WriteLine($"Producing #{i}");
if (i == 15) throw new Exception("Oops!");
foreach (int[] chunk in Produce().ChunkNonDestructive(10))
Console.WriteLine($"Consumed: [{String.Join(", ", chunk)}]");
private static IEnumerable<TOutput> DeferErrorUntilCompletion<TInput, TOutput>(
IEnumerable<TInput> input,
Func<IEnumerable<TInput>, IEnumerable<TOutput>> conversion)
Task errorContainer = null;
IEnumerable<TInput> InputIterator()
using var enumerator = input.GetEnumerator();
if (!enumerator.MoveNext()) break;
item = enumerator.Current;
errorContainer = Task.FromException(ex);
IEnumerable<TOutput> output = conversion(InputIterator());
foreach (TOutput item in output) yield return item;
errorContainer?.GetAwaiter().GetResult();
public static IEnumerable<TSource[]> ChunkNonDestructive<TSource>(
this IEnumerable<TSource> source, int size)
ArgumentNullException.ThrowIfNull(source);
if (size < 1) throw new ArgumentOutOfRangeException(nameof(size));
return DeferErrorUntilCompletion(source, s => s.Chunk(size));