C# defines two interfaces to support enumeration. The namespace System.Collections contains these declarations,
IEnumerator and IEnumerable declarations
- public interface IEnumerator
- {
- bool MoveNext();
- object Current
- {
- get;
- }
- void Reset();
- }
- public interface IEnumerable
- {
- IEnumerator GetEnumerator();
- }
The code below defines a class that can be enumerated in this way. As you can see, the CountdownEnumerator class is more complex, and it implements the enumeration logic in a single place. In this sample, the enumerator does not really enumerate anything but simply returns descending numbers starting from the StartCountdown number defined in the Countdown class (which is also the enumerated class).
Enumerable class
- public class Countdown: IEnumerable
- {
- public int StartCountdown;
- public IEnumerator GetEnumerator()
- {
- return new CountdownEnumerator(this);
- }
- }
- public class CountdownEnumerator: IEnumerator
- {
- private int _counter;
- private Countdown _countdown;
- public CountdownEnumerator(Countdown countdown)
- {
- _countdown = countdown;
- Reset();
- }
- public bool MoveNext()
- {
- if (_counter > 0)
- {
- _counter--;
- return true;
- }
- else
- {
- return false;
- }
- }
- public void Reset()
- {
- _counter = _countdown.StartCountdown;
- }
- public object Current
- {
- get
- {
- return _counter;
- }
- }
- }
Sample enumeration code
- public class DemoEnumerator
- {
- public static void DemoCountdown()
- {
- Countdown countdown = new Countdown();
- countdown.StartCountdown = 5;
- IEnumerator i = countdown.GetEnumerator();
- while (i.MoveNext())
- {
- int n = (int) i.Current;
- Console.WriteLine(n);
- }
- i.Reset();
- while (i.MoveNext())
- {
- int n = (int) i.Current;
- Console.WriteLine("{0} BIS", n);
- }
- }
- // …
- }
Note: C# 2.0 introduced enumeration support through generics. The namespace System.Collections.Generic contains generic IEnumerable<T> and IEnumerator<T> declarations.
These interfaces eliminate the need to convert data in and out from an object type. This capability is important when enumerating value types because there are no more box or unbox operations that might affect performance.
Enumeration using a foreach statement
- public class DemoEnumeration
- {
- public static void DemoCountdownForeach()
- {
- Countdown countdown = new Countdown();
- countdown.StartCountdown = 5;
- foreach(int n in countdown)
- {
- Console.WriteLine(n);
- }
- foreach(int n in countdown)
- {
- Console.WriteLine("{0} BIS", n);
- }
- }
- // …
- }
Note: The foreach statement can also be used with classes that do not expose an IEnumerable interface but that have a public GetEnumerator method.
C# 2.0 introduced the yield statement through which the compiler automatically generates a class that implements the IEnumerator interface returned by the GetEnumerator method. The yield statement can be used only immediately before a return or break keyword. The code in below code generates a class equivalent to the previous CountdownEnumerator.
Enumeration using a yield statement
- public class CountdownYield: IEnumerable
- {
- public int StartCountdown;
- public IEnumerator GetEnumerator()
- {
- for (int i = StartCountdown - 1; i >= 0; i--)
- {
- yield
- return i;
- }
- }
- }
A method that contains yield statements is called an iterator. An iterator can include many yield statements. The code in below is perfectly valid and is functionally equivalent to the previous CountdownYield class with a StartCountdown value of 5.
Multiple yield statements
- public class CountdownYieldMultiple: IEnumerable
- {
- public IEnumerator GetEnumerator()
- {
- yield
- return 4;
- yield
- return 3;
- yield
- return 2;
- yield
- return 1;
- yield
- return 0;
- }
- }
Enumeration using yield (typed)
- public class CountdownYieldTypeSafe: IEnumerable < int >
- {
- public int StartCountdown;
- IEnumerator IEnumerable.GetEnumerator()
- {
- return this.GetEnumerator();
- }
- public IEnumerator < int > GetEnumerator()
- {
- for (int i = StartCountdown - 1; i >= 0; i--)
- {
- yield
- return i;
- }
- }
- }
The internal implementation of LINQ to Objects makes extensive use of enumerations and yield. Even if they work under the covers, keep their behavior in mind while you are debugging code.

Pradeep SahooPosted May 20, 2016, 7:58 AM
Well written. .
Bhuvanesh MohankumarPosted May 18, 2016, 7:42 AM
Good one...