IEnumerable and IEnumerable are standard interfaces defined in C# which are used to enumerate the both generic and non-generic collections and we can iterate over these collection in forward only manner. Both are very related with each other in working in the .NET Framework.
IEnumerator:
IEnumerator defines the way to traverse the elements of the collection in forward-only manner. The definition of IEnumerator contains just one property named Current which returns the current item of collection being enumerated and two methods named MoveNext() and Reset().
MoveNext() moves the position to next element and returns bool indicating that is there any more elements and will return false if no more elements are found in collection, MoveNext() needs to be called before accessing first element.
Reset() method which is pretty explanatory that it resets the enumerator and moves position back to the first element of the sequence so that it can be enumerated again.
Here is the definition of it:
- public interface IEnumerator
- {
- boolMoveNext();
- object Current
- {
- get;
- }
- void Reset();
- }
IEnumerable:
Normally Collection do not provide implementation of enumerators, instead they use IEnumerableinterface for providing enumerators.
The following is the definition of IEnumerableinterface:
- public interface IEnumerable
- {
- IEnumeratorGetEnumerator();
- }
Collection classes provide implementation for this interface and it just returns the specific IEnumerator instance for the collection.
Example:
The following example will help you understand how these two interfaces work together, we will be using a String type as an example as it is a framework provided type and it implements IEnumerableinterface which means we can enumerate on string variables.
- string enumeration = "Enumeration";
Now as string provides implementation for IEnumerable, i can call GetEnumerator() on it to get the enumerator of string:
- IEnumerator enumerator = enumeration.GetEnumerator();
As we have enumerator of string available now, we can enumerate on it and print each character on screen:
- while (enumerator.MoveNext())
- {
- char c = (char) enumerator.Current;
- Console.Write(c);
- }
Enumeration
But normally when we are working we do not need to call these methods like we did above, because C# provide us foreach loop to do that more cleaner way.
The above code can be written as :
- string enumeration = "Enumeration";
-
- foreach(char character in enumeration)
- Console.Write(character);
IEnumerable<T> and IEnumerator<T>:
These two interfaces are almost same in implementation as IEnumerable and IEnumerator except they have generic type parameter.
Here is the implementation:
- public interface IEnumerator < T > : IEnumerator, IDisposable
- {
- T Current
- {
- get;
- }
- }
- public interface IEnumerable < T > : IEnumerable
- {
- IEnumerator < T > GetEnumerator();
- }
The typed parameter with these interfaces provides type safety at compile time and also eliminates the overhead of boxing and unboxing of value types and they are also more convenient to the consumers of it.
Arrays in c# by default implement IEnumerable<T> where T is the member type of array.
Type safe implementations of IEnumerable and IEnumerator saves us from runtime errors by throwing errors at compile time if something illegal is written.
For Example:
- void Foo (IEnumerable<int> numbers)
- {
-
- }
If i call above method by passing parameter of IEnumerable<char> it will give compile time error.
The standard practice for Collections is to expose the IEnumerable<T>and hiding the nongenericIEnumerable using explicit interface implementation. It is because if you call GetEnumerator() directly it will return generic IEnumerator<T> which is type safe, but sometimes this rule is broken for backward compatibility scenarios as Generics were introduced after C# 2.0.
Arrays are classical example for this, as they return non-generic IEnumerator to avoid breaking of old code base which were written in C# 2.0 or older than 2.0
For getting generic IEnumerator<T> you have to explicitly cast to expose the interface. For example:
- int[] numbers =
- {
- 1,
- 2,
- 3
- };
- var enumerator = ((IEnumerable < int > ) numbers).GetEnumerator();
But typically we don’t need to do this ourselves as foreach statement do this for us.