.NET Fiddle

CSharp · Console

/Program.cs

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
	public static void Main()
	{
		var a = Enumerable.Range(0, 11).ToArray();
		Console.Write($"Batched list:\n\t");
		Console.WriteLine(a.Batch(3).Select(i => i.Join(",")).Join(";"));
		Console.Write($"First from each batch:\n\t");
		Console.WriteLine(a.Batch(3).Select(i => i.First()).Join(","));
		Console.Write($"Second from each batch:\n\t");
		Console.WriteLine(a.Batch(3).Select(i => i.Skip(1).First()).Join(","));
		// This still fails!
		// Console.WriteLine(a.Batch(3).ToList().Select(i => i.Join(",")).Join(";"));
		Console.Write($"Number of batches:\n\t");
		Console.WriteLine(a.Batch(3).Count());
		Console.Write($"Number of full batches:\n\t");
		Console.WriteLine(a.Batch(3).Where(i => i.Count() == 3).Count());
		Console.Write($"Buffering to access prior batches:\n\t");
		Console.WriteLine(a.Batch(3).Buffer().Last().Join(","));
	}
}

public static class BatchLinq
{
	public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size)
	{
		if (source == null)
			throw new ArgumentNullException("source");
		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 iterator 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();
				
				// discard skipped items
				while (++i < size && enumerator.MoveNext());
			}
	}

	public static IEnumerable<IReadOnlyList<T>> Buffer<T>(this IEnumerable<IEnumerable<T>> batched) => batched.Select(e => e.ToList());
	
	public static string Join<T>(this IEnumerable<T> s, string j)
	{
		return string.Join(j, s.ToArray());
	}
}
;