C#

IEnumerable and IEnumerator Interfaces

IEnumerable and IEnumerator Interfaces in C# have been implemented by a number of collection classes. Basically, these interfaces provide the functionality for traversing the elements of a collection.

Basically, IEnumerator is something that can enumerate or visit the elements of a collection. Therefore, if any class implements IEnumerator, it has the ability to enumerate a collection. On the other hand, IEnumerable is something that can be enumerated. Hence, when a class implements IEnumerable, we can enumerate it.

However, IEnumerator only makes the elements of collection accessible. In no way, it allows manipulation or modification of the collection or its elements.

Details of IEnumerable and IEnumerator Interfaces

As far as the structure of these interfaces is concerned, the IEnumerable only contains a method called as GetEnumerator(). While IEnumerable contains a property called Current and two methods. While the first one is called MoveNext(), the other one is called Reset().

Consequently, if we change the functionality of the IEnumerator, then we can also change the flow of the foreach loop. For instance, we can make the foreach loop iterating the elements of a collection in reverse order. In another case, we can make iterations in the foreach loop to skip some of the elements also. However, in any case, the foreach loop can never modify the elements of the collection.

Now, we discuss the elements of the IEnumerator interface. As mentioned earlier, this interface has a property called Current. Basically, it is a read-only property that returns the value of the current object.

Similarly, the MoveNext() method moves the cursor to the next element in the collection and returns true if the element is available. Once, it reaches the end of the collection, the MoveNext() method returns false.

Finally, there is also a Reset() method that moves the cursor before the first element of the collection in its default implementation.

Accordingly, we have a method called GetEnumerator() in the IEnumerable interface that creates an enumerator.

The Foreach Loop

As can be seen, both of these interfaces provide an important feature in C#. It is known as foreach loop. In fact, these two interfaces hide all the complexities behind the implementation of the foreach loop. In other words, the foreach loop uses the implementations of these two interfaces to provide iteration of elements of a collection.

Implementing IEnumerable and IEnumerator Interfaces

As an illustration, the following example shows the implementation of these two interfaces. Basically, the following example shows how to enumerate the array of objects of the class called MyClass. Further, class A implements IEnumerable, whereas class B implements IEnumerator. As shown, the Main() method first displays the array elements using the default implementation of the foreach loop. After that, we create an instance of class A which is the implementation of the IEnumerable interface, and pass the array in the constructor. Now, the foreach loop makes use of the custom implementation of these interfaces.

using System;
using System.Collections;
namespace EnumeratorDemo
{
    class MyClass
    {
        int x, y;
        public MyClass() { x = 1; y = 1; }
        public MyClass(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
        public override string ToString()
        {
            return $"x = {x}, y= {y}";
        }
    }

    class A:IEnumerable
    {
        private MyClass[] list1;
        public A(MyClass[] list2)
        {
            list1 = new MyClass[list2.Length];
            for(int i=0;i<list2.Length;i++)
            {
                list1[i] = list2[i];
            }
        }
        public B GetEnumerator()
        {
            Console.WriteLine("Creating an enumerator...");
            return new B(list1);
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            Console.WriteLine("Calling IEnumerable.GetEnumerator()...");
            return (IEnumerator)GetEnumerator();
        }
    }
    class B:IEnumerator
    {
        public MyClass[] list3;
        int cur_pos = -1;
        public B(MyClass[] list4)
        {
            list3 = list4;
        }
        public bool MoveNext()
        {
            Console.WriteLine("Next Element...");
            cur_pos++;
            return cur_pos < list3.Length;
        }
        public void Reset()
        {
            cur_pos = -1;
        }
        public MyClass Current
        {
            get
            {
                try
                {
                    Console.WriteLine("Fetching value of Current Object...");
                    return list3[cur_pos];
                }
                catch(IndexOutOfRangeException ex)
                {
                    Console.WriteLine(ex.Message);
                    throw new InvalidOperationException();
                }
            }
        }

        Object IEnumerator.Current
        {
            get
            {
                return Current;
            }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            MyClass[] arr = new MyClass[20];
            for(int i=0;i<20;i++)
            {
                arr[i] = new MyClass(i + 1, i + 2);
            }

            //Display the array
            Console.WriteLine("Display array...");
            foreach (MyClass ob in arr)
                Console.WriteLine(ob);

            //Using IEnumerator and IEnumerable Implementation
            A ob1 = new A(arr);
            Console.WriteLine("Again Displaying the array...");
            foreach (MyClass ob in ob1)
                Console.WriteLine(ob);
        }
    }
}

Output

Implementation of IEnumerable and IEnumerator Interfaces
Implementation of IEnumerable and IEnumerator Interfaces

Reverse Iteration

In the same way, we can make the foreach loop iterate in reverse order. In fact, we can do it easily by making certain changes in the implementation of the IEnumerator interface. Firstly, we set the variable cur_pos to the length of the collection. In other words, cur_pos will now refer to the index after the last element of the collection. Similarly, we decrement it in the MoveNext() method and also change the conditional expression in the same method. Therefore, the MoveNext() method will return true till the value of cur_pos is more than equal to zero. Finally, in the Reset() method, we set the cur_pos so that it will have an index value one more than the index of the last element.

using System;
using System.Collections;
namespace EnumeratorDemo1
{
        class MyClass
        {
            int x, y;
            public MyClass() { x = 1; y = 1; }
            public MyClass(int x, int y)
            {
                this.x = x;
                this.y = y;
            }
            public override string ToString()
            {
                return $"x = {x}, y= {y}";
            }
        }

        class A : IEnumerable
        {
            private MyClass[] list1;
            public A(MyClass[] list2)
            {
                list1 = new MyClass[list2.Length];
                for (int i = 0; i < list2.Length; i++)
                {
                    list1[i] = list2[i];
                }
            }
            public B GetEnumerator()
            {
                Console.WriteLine("Creating an enumerator...");
                return new B(list1);
            }
            IEnumerator IEnumerable.GetEnumerator()
            {
                Console.WriteLine("Calling IEnumerable.GetEnumerator()...");
                return (IEnumerator)GetEnumerator();
            }
        }
        class B : IEnumerator
        {
            public MyClass[] list3;
            int cur_pos = -1;
            public B(MyClass[] list4)
            {
                list3 = list4;
                cur_pos = list3.Length;
            }
            public bool MoveNext()
            {
                Console.WriteLine("Next Element...");
                cur_pos--;
                return cur_pos >= 0;
            }
            public void Reset()
            {
                cur_pos = list3.Length;
            }
            public MyClass Current
            {
                get
                {
                    try
                    {
                        Console.WriteLine("Fetching value of Current Object...");
                        return list3[cur_pos];
                    }
                    catch (IndexOutOfRangeException ex)
                    {
                        Console.WriteLine(ex.Message);
                        throw new InvalidOperationException();
                    }
                }
            }

            Object IEnumerator.Current
            {
                get
                {
                    return Current;
                }
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                MyClass[] arr = new MyClass[20];
                for (int i = 0; i < 20; i++)
                {
                    arr[i] = new MyClass(i + 1, i + 2);
                }

                //Display the array
                Console.WriteLine("Display array...");
                foreach (MyClass ob in arr)
                    Console.WriteLine(ob);

                //Using IEnumerator and IEnumerable Implementation
                //Iterating in Reverse Order
                A ob1 = new A(arr);
                Console.WriteLine("Again Displaying the array in the reverse order...");
                foreach (MyClass ob in ob1)
                    Console.WriteLine(ob);
            }
        }
    }

Output

Iterating in Reverse Order using IEnumerable and IEnumerator Interfaces
Iterating in Reverse Order using IEnumerable and IEnumerator Interfaces

Iterating Alternate Elements

In like manner, we can also change the MoveNext() method to skip the iteration of certain elements of the collection. In fact, we can make the foreach loop to visit only the alternate elements of the collection. Therefore, we just change the expression in the MoveNext() method that increments the variable cur_pos as follows.

cur_pos=cur_pos+2;

Now, the foreach loop will only visit every alternate element of the collection starting from its second element. The following example demonstrates the iteration of alternate elements of a collection using foreach loop.

using System;
using System.Collections;
namespace EnumeratorDemo2
{
        class MyClass
        {
            int x, y;
            public MyClass() { x = 1; y = 1; }
            public MyClass(int x, int y)
            {
                this.x = x;
                this.y = y;
            }
            public override string ToString()
            {
                return $"x = {x}, y= {y}";
            }
        }

        class A : IEnumerable
        {
            private MyClass[] list1;
            public A(MyClass[] list2)
            {
                list1 = new MyClass[list2.Length];
                for (int i = 0; i < list2.Length; i++)
                {
                    list1[i] = list2[i];
                }
            }
            public B GetEnumerator()
            {
                Console.WriteLine("Creating an enumerator...");
                return new B(list1);
            }
            IEnumerator IEnumerable.GetEnumerator()
            {
                Console.WriteLine("Calling IEnumerable.GetEnumerator()...");
                return (IEnumerator)GetEnumerator();
            }
        }
        class B : IEnumerator
        {
            public MyClass[] list3;
            int cur_pos = -1;
            public B(MyClass[] list4)
            {
                list3 = list4;
            }
            public bool MoveNext()
            {
                Console.WriteLine("Next Element...");
                cur_pos=cur_pos+2;
                return cur_pos < list3.Length;
            }
            public void Reset()
            {
                cur_pos = -1;
            }
            public MyClass Current
            {
                get
                {
                    try
                    {
                        Console.WriteLine("Fetching value of Current Object...");
                        return list3[cur_pos];
                    }
                    catch (IndexOutOfRangeException ex)
                    {
                        Console.WriteLine(ex.Message);
                        throw new InvalidOperationException();
                    }
                }
            }

            Object IEnumerator.Current
            {
                get
                {
                    return Current;
                }
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                MyClass[] arr = new MyClass[20];
                for (int i = 0; i < 20; i++)
                {
                    arr[i] = new MyClass(i + 1, i + 2);
                }

                //Display the array
                Console.WriteLine("Display array...");
                foreach (MyClass ob in arr)
                    Console.WriteLine(ob);

                //Using IEnumerator and IEnumerable Implementation
                //Iterating alternate elements
                A ob1 = new A(arr);
                Console.WriteLine("Displaying the alternate array elements...");
                foreach (MyClass ob in ob1)
                    Console.WriteLine(ob);
            }
        }
    }

Output

Iterating Alternate Elements of a Collection using IEnumerable and IEnumerator Interfaces
Iterating Alternate Elements of a Collection using IEnumerable and IEnumerator Interfaces

Summary

This article on IEnumerable and IEnumerator Interfaces provides a brief discussion on these collection interfaces in C#. Generally, the collection classes implement interfaces so that their elements can be enumerated. It is important to realize that the foreach loop uses the functionality of these interfaces to iterate through the elements of the corresponding collection. Also, in order to alter the flow of foreach loop, we need to make changes in the implementation of the IEnumerator interface.


Related Topics

A Beginner’s Tutorial on WPF in C#

Everything about Tuples in C# and When to Use?

Linear Search and Binary Search in C#

Creating Jagged Arrays in C#

Learning Indexers in C#

Understanding Method Parameter Modifiers in C#

Object Initializers in C#

Examples of Static Constructors in C#

When should We Use Private Constructors?

C# Practice Questions

C# Basic Examples

Private and Static Constructors in C#

Constructors in C#

C# Arrays

C# Examples

How to Create a C# Console Application

Creating Navigation Window Application Using WPF in C#

LINQ To SQL Examples

Understanding the Concept of Nested Classes in C#

How to Setup a Connection with SQL Server Database in Visual Studio

Examples of Extension Methods in C#

Learning All Class Members in C#

Access Modifiers in C#

C# Root Class – Object

KeyValuePair and its Applications

Leave a Reply

Your email address will not be published. Required fields are marked *