 <p>The best way to do this is to use something like <a href="http://nuget.org/packages/MoreLinq.Source.MoreEnumerable.Batch/" rel="nofollow">the <code>Batch</code> method from <code>MoreLinq</code></a>.</p> <p>This lets you partition the items in a sequence into batches of a specified size.</p> <p>If you want a simple approach which doesn't need to be threadsafe (i.e. you don't need to use it with <code>Parallel.ForEach()</code> for example) then you can use the following extension method.</p> <p>It has the advantage that you can produce all the batches without calling Skip multiple times:</p> <pre><code>public sealed class Batch&lt;T&gt; { public readonly int Index; public readonly IEnumerable&lt;T&gt; Items; public Batch(int index, IEnumerable&lt;T&gt; items) { Index = index; Items = items; } } public static class EnumerableExt { // Note: Not threadsafe, so not suitable for use with Parallel.Foreach() or IEnumerable.AsParallel() public static IEnumerable&lt;Batch&lt;T&gt;&gt; Partition&lt;T&gt;(this IEnumerable&lt;T&gt; input, int batchSize) { var enumerator = input.GetEnumerator(); int index = 0; while (enumerator.MoveNext()) yield return new Batch&lt;T&gt;(index++, nextBatch(enumerator, batchSize)); } private static IEnumerable&lt;T&gt; nextBatch&lt;T&gt;(IEnumerator&lt;T&gt; enumerator, int blockSize) { do { yield return enumerator.Current; } while (--blockSize &gt; 0 &amp;&amp; enumerator.MoveNext()); } } </code></pre> <p>And you use it like:</p> <pre><code> var items = Enumerable.Range(100, 510); // Pretend we have 50 items. int itemsPerPage = 20; foreach (var page in items.Partition(itemsPerPage)) { Console.Write("Page " + page.Index + " items: "); foreach (var i in page.Items) Console.Write(i + " "); Console.WriteLine(); } </code></pre> <p>But if you need threadsafe partitioning, use the MoreLinq Batch method I linked above.</p>