How it works
IEnumerable<T> describes a source that can provide an enumerator. Implementing it lets a type participate in foreach and LINQ without promising random access, a known count, or a mutable list interface. Yield return allows the compiler to create the enumerator state machine for a simple sequential implementation.
An iterator method executes as enumeration advances, rather than eagerly producing every result when the method is called. This means validation placed inside an iterator can also be deferred. Put constructor-level invariants in the constructor when callers should learn about invalid configuration immediately. The sequence in this lesson stores only a starting value and a nonnegative count, so each enumeration can produce the same finite values independently.
Walk through the example. NumberWindow receives a start of four and a count of three. Its generic enumerator yields start plus each offset, producing four, five, and six. The explicit nongeneric interface implementation delegates to the same enumerator so the type satisfies both enumerable contracts. A second LINQ enumeration calculates fifteen, demonstrating that the source creates a fresh traversal rather than consuming one permanent iterator.
Watch the boundary. An enumerable is not guaranteed to be repeatable merely because this example is. Other sources can observe mutable state, perform expensive work, or represent a one-time stream. Callers should rely on the source's documented contract. Also avoid exposing a live mutable collection while claiming that the iterator represents a stable snapshot; determine whether later changes should be observed or excluded.
Practice independently. Add validation for a negative count and test a zero-length window. Verify that two separate enumerations produce the same results for this type. Then create a version backed by a mutable list and decide whether the constructor copies the list or observes future changes. Explain how that ownership choice changes the promises a consumer may safely make about repeated enumeration.
A working example
using System;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
var window = new NumberWindow(4, 3);
Console.WriteLine(string.Join(",", window));
Console.WriteLine(window.Sum());
sealed class NumberWindow : IEnumerable<int>
{
private readonly int start, count;
public NumberWindow(int start, int count)
{
if (count < 0) throw new ArgumentOutOfRangeException(nameof(count));
this.start = start; this.count = count;
}
public IEnumerator<int> GetEnumerator()
{
for (int offset = 0; offset < count; offset++) yield return start + offset;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
}
Expected output
4,5,6 15
IEnumerable promises traversal, not indexing or repeatability; document those additional guarantees separately.