The correct implementation of @Bradley Uffner and @NetMage non allocating iterator method is like this:
public static IEnumerable> GroupConsecutive(this IEnumerable source)
{
using (var e = source.GetEnumerator())
{
for (bool more = e.MoveNext(); more; )
{
int first = e.Current, last = first, next;
while ((more = e.MoveNext()) && (next = e.Current) > last && next - last == 1)
last = next;
yield return Enumerable.Range(first, last - first + 1);
}
}
}
It works correctly even for unordered input, iterates the source sequence only once and handles correctly all corner cases and integer over/underflow. The only case it fails is for consecutive range count bigger than int.MaxValue.
But looking at your follow up question, probably the following implementation will better suit your needs:
public static IEnumerable<(int First, int Last)> ConsecutiveRanges(this IEnumerable source)
{
using (var e = source.GetEnumerator())
{
for (bool more = e.MoveNext(); more;)
{
int first = e.Current, last = first, next;
while ((more = e.MoveNext()) && (next = e.Current) > last && next - last == 1)
last = next;
yield return (first, last);
}
}
}