Go4Expert

Go4Expert (http://www.go4expert.com/)
-   C# (http://www.go4expert.com/articles/c-sharp-tutorials/)
-   -   Enumeration Collections IEnumerable and IEnumerator in C# (http://www.go4expert.com/articles/enumeration-collections-ienumerable-t30023/)

shabbir 9Mar2014 19:34

Enumeration Collections IEnumerable and IEnumerator in C#
 
.NET framework provides wide range of namespaces that contain types, interfaces and structs which can be used to store, manage, and sort collections of data. These types include, lists, arrays, linked list and sorted as well as unsorted dictionaries. The types of collections available in .NET framework may differ greatly in terms of complexity; for example, an array and linked list are considered relatively simpler being linear data structures, as compared to non-linear data structures such as hash tables and red/black trees. These data structures have different characteristics and properties but traversing a data structure is common amongst all of the major data structures. For this purpose .NET framework provides two interfaces i.e IEnumerable and IEnumerator along with the generic versions i.e. IEnumerable<T> & IEnumerator <T> for generic collections. The collections implement these interfaces allowing the developers to use a common API to traverse on different collection types. In this article we are going to explain in detail how enumeration can be performed on variety of types in .NET using these interfaces.

IEnumerator



The IEnumerator has two methods and a property. The declaration of the IEnumerator interface is as follows
Code:

public interface IEnumerator
{
    bool MoveNext();
    object Current { get; }
    void Reset();
}

Here in this code, the MoveNext() method moves the cursor to the next item in the list. It will keep returning true until there are more elements in the list. This is basically a way to point to an element. Note that you cannot fetch the element using MoveNext() method, it is only a way to point to element. Another important thing to remember here is that MoveNext() method is used to move the cursor to the forward direction only. You cannot move backwards.

Current is used to access the object at the current index i.e, the index towards which MoveNext() method is pointing to. It returns an object type which we usually cast before using in our application. An important thing to note here is that before getting the first element using Current, call the MoveNext() method once, because this is useful when we have empty collections.

The last part of this interface is the Reset() method which resets the cursor back to the first element in the collection or makes MoveNext() method points to the starting index of any collection.

IEnumerable



The collection types do not directly implement IEnumerator interface; rather they implement IEnumerable interface which returns the IEnumerator. The declaration of IEnumerable interface is as follows.
Code:

public interface IEnumerable
{
    IEnumerator GetEnumerator();
}

Exposing numerator using IEnumerable interface ensures that different consumers can simultaneously access the collection. IEnumerable is the most basic interface that every class that contains collection of elements, implement. Now, with the help of a basic example, we will explain how IEnumerable interface can be used. Have a look at our first example.

Example1
Code:

using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Numerics;

namespace CSharpTutorial
{
    class Program
    {
        public static void Main()
        {
            IEnumerator numer = "Go4Expert".GetEnumerator();

            while (numer.MoveNext())
            {
                Console.Write((char)numer.Current+".");
            }
            Console.ReadLine();
        }
    }
}

In Example1, we have a string named “Go4Expert”, string is also actually a collection of characters, therefore it implements the IEnumerable interface by default. Thus, we can call GetEnemurater on string which will return an IEnumerator object. In Example1, we have named that object ‘numer’. We have a while loop which will execute until numer.MoveNext() returns true, and it will keep returning true until all the elements in the string “Go4Expert” have been traversed. Inside the while loop we are getting each element to which the MoveNext() method points, using ‘Current’, it will return an object containing current character of the string. We have cast that object into character and have displayed it on screening. Each time we display a character we append a dot with it. The output of the code in Example1 is as follows:

Output1

http://imgs.g4estatic.com/c-sharp/ie...or/output1.png

Using IEnumerator<T> and IEnumerable <T> for Generic Collections



IEnumerable<T> and IEnumerator<T> serves same purpose for generic collections as served by simple IEnumerable and IEnumerator for simple collections. The declaration for IEnumerator <T> is as follows.
Code:

public interface IEnumerator <T>  : IEnumerator, IDisposable
{
    T  Current { get; }
}

Similarly, the declaration for IEnumerable also involves the type T as follows
Code:

public interface IEnumerable<T> : IEnumerable
{
    IEnumerator<T> GetEnumerator();
}

Implementing these interfaces is more convenient as compared to simple ones because they ensure static type safety which lies at the core of .NET framework. Also, you saw that in simple collections we had to cast object into a particular type in order to use; with these typed interfaces you don’t need to cast the object as the interface variable already know, what type of objects it has to hold. We specify the type of the array when we declare it, so by default the array would implement IEnumerable <T> interface, where in this case T would be the type of the array.

Usually, generic classes expose only the IEnumerable<T> interface rather than IEnumerable interface, therefore if you call GetEnumerator() on any generic collection type, it will return IEnumerator<T> rather than IEnumerator, though both are available. This is due to the reason that most of advanced generic collections hide IEnumerable interface, and rightly so because .NET framework fosters type safety at compile time.

Another benefit of using IEnumerable <T> is that it inherits from a very important interface i.e IDisposable which allows holding and releasing of resources after usage. Thus, it allows IEnumerable <T> to hold important resources such as database connections which has to be released once all the elements have been enumerated. For example you can use following line of code on IEnumerable <T> but not on IEnumerable.

Why Implement Enumerations interfaces?



Before we explain how we can implement Enumerations in our custom collections, let us first discuss why one would want to implement these interfaces in an application.
  • Enumeration interfaces allow you to traverse your custom collections using the foreach loop.
  • It allows you to interoperate with different structs and types as a standard collection.
  • It allows you to use collection initializers in your custom collection.
  • Some application requires sophisticated collection interfaces that are implemented using enumeration interfaces that we mentioned above.

How to implement Enumeration Interfaces?



Now a question arises that how you can implement enumeration interfaces on your custom types. In order to implement an IEnumerable or IEnumerable<T> interface you have to return an enumerator whenever the GetEnumerator method is called. You can return an enumerator in three ways.
  1. If your custom type wraps another collection in it then you can return enumerator by return the enumerator of the collection that has been wrapped by your type.
  2. You can also use ‘yield return’ that will return an Iterator.
  3. You can also instantiate your own IEnumerator and IEnumerator<T> interface to return an enumerator.
The first method is extremely simple, you will just have to call the GetEnumerator() method on the inner collection and it will return a numerator which you can use to implement enumeration interfaces in your custom type. However, this method is not flexible and when you want your custom type to work like the inner collection.

A more practical and feasible approach is to use ‘yield return’ statement to write an iterator which is equal to returning an enumerator in impact. To see how yield return method returns an enumerator, have a look at the Example2.

Example2
Code:

using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Numerics;

namespace CSharpTutorial
{
    class Program
    {
        public static void Main()
        {
            CustomCollection col = new CustomCollection();

            IEnumerator numer = col.GetEnumerator();

            while (numer.MoveNext())
            {
                Console.Write((string)numer.Current+ ".");
            }
            Console.ReadLine();
        }
    }

    class CustomCollection : IEnumerable
    {
        string[] arr = { "Go", "For", "Expert", "Is", "Best", "Online", "Resource" };

        public IEnumerator GetEnumerator()
        {

            foreach (string s in arr)
            yield return s;
        }   
    }
}

Look at Example2, for the time being jump straight to the CustomCollection collection that is below the Program class. The CustomCollection class implements the IEnumerable interface therefore we will have to implement the GetEnumerator function of this class. But first, we have a string array which we have named arr, this array store strings. We have initialized this array with some literal values. Now come towards the GetEnumerator method, in this method, we are simply iterating on the string type array ‘arr’ using for each loop and during each iteration we are writing ‘yield return s’.

You must be wondering that the GetEnumerator() method has return type of IEnumerator but inside the method we are not returning any IEnumerator object. Here is where the ‘yield return’ plays its rule. Actually when compiler comes across the ‘yield return’ it actually constructs a nested hidden IEnumerator class and when all the iterations of the foreach loop complete and the control is about to exit the GetEnumerator() method, the compiler instantiates that hidden IEnumerator class and returns that back to the calling method. The enumerator contains a collection of the elements that were yield returned i.e. all the elements of the string array ‘arr’ can be traversed by calling GetEnumerator() on the CustomCollection class by virtue of this yield return.

Now, come to the static main method of the Program class, here inside that method we instantiated CustomeCollection class by created an object which we named ‘col’. We then called GetEnumerator() method which returned us an enumerator which we stored in ‘numer’ object of IEnumerator type. We then called MoveNext() method on ‘numer’ object. Inside the while loop we fetched the current object in the ‘numer’ enumerator by calling Current. We parsed it into string and then displayed it by appending a dot after each word of the string. The output of the code in Example2 is as follows. The output of the code in Example2 is as follows

Output2

http://imgs.g4estatic.com/c-sharp/ie...or/output2.png

In Example2, we implement general enumeration interface. However this concept can be extended to implement interface for generic custom collection types as well. It will need slight modifications though. Have a look at Example3 of to grasp this concept.

Example3

Code:

using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Numerics;

namespace CSharpTutorial
{
    class Program
    {
        public static void Main()
        {
            CustomCollection col = new CustomCollection();

            IEnumerator numer = col.GetEnumerator();

            while (numer.MoveNext())
            {
                Console.Write(numer.Current+ ".");
            }
            Console.ReadLine();
        }
    }

    class CustomCollection : IEnumerable<int>
    {
        int[] arr = { 1, 3, 5, 7, 9 };

        public IEnumerator<int> GetEnumerator()
        {
            foreach (int i in arr)
            yield return i;
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
}

Again come straight to the CustomCollection, in this case the class implements IEnumerable <int> interface so it has to contain a GetEnumerator() method that returns a generic IEnumerator<T>. In addition to that, since IEnumerable<T> interface also inherits from IEnumerable interface, therefore we will have to implement GetEnumerator() method of IEnumerable interface too. So, In CustomCollection class, we have an integer type array which we named ‘arr’. We initialized this array with the first 5 odd numbers. Then we wrote GetEnumerator() method whose return type is IEnumerator<T>. This method belongs to IEnumerable<T> interface that is implemented by our CustomCollection class. Inside the method we iterated the int array ‘arr’ using foreach and then returned the enumerator using ‘yield return’ as we did in the Example2. Next we have to implement the GetEnumerator() method of IEnumerable interface too, which will return simple enumerator IEnumerator. Iniside this method we have simply called the GetEnumerator() method of the IEnumerable<T> interface which will return ‘yield return’. Therefore, our final returned value would be enumerator. In the main function, we have not changed much from Example2, we have same object ‘col’ of type CustomCollection and we have IEnumerator<int> instead of simple IEnumerator to ensure type safety and then we have displayed these first five odd numbers separated by a dot on the screen. The output of the code in Example3 is as follows.

Output3

http://imgs.g4estatic.com/c-sharp/ie...or/output3.png

These are some of the ways that can be used to implement enumeration interfaces in your custom classes. Apart from this method, in order to have more flexibility over your custom type, you can implement IEnumerable and IEnumerator interfaces along with their generic counterparts and provide the definition for Current and Reset() method as well, in addition to GetEnumerator() method which we did in Example2 & Example3.


All times are GMT +5.5. The time now is 18:41.