IEnumerable and IEnumerator in C#

IEnumerable and IEnumerator in C#

IEnumerable and IEnumerator interfaces are two things that confuse many novice C# developers. In this post, I’m going to explain these two interfaces.

What are IEnumerable and IEnumerator

In short, IEnumerable and IEnumerator are two interfaces that are used to implement iteration in .NET. In C#, all collections such as lists, dictionary, etc implement the IEnumerable interface. So that they can be Iterated.

So we can infer that any class that implements the IEnumerable interface can be enumerated. That is, you can use a foreach loop to iterate over the class.

Why should you use these Interfaces?

Now you might ask we can iterate over any member of a class by making it public. So, why should I implement these interfaces?

Let me answer your question with an example. Consider the following code:

namespace CSConsole
{
     class Items
     {
         public string[] ItemList = new string[10];
         int pointer = 0;
         public void AddItem(string item)
         {
             ItemList[pointer++] = item;
         }
         public void RemoveItem(int index)
         {
             ItemList[index] = "";
         }
     }
     
      class Program 
     {
         static void Main(string[] args)
         {
             Items i = new Items();
             i.AddItem("Item 1");
             i.AddItem("Item 2");
             i.AddItem("Item 3");

             foreach(string item in i.ItemList)
             {
                 Console.WriteLine(item);
             }
             Console.ReadKey();
         }
     }
}

The problem with this code is that it violates the information hiding principle by exposing its internal structure. And if we declare all the methods and members as private to address information hiding, we won’t be able to iterate over the list ( Private members cannot be accessed by another class ). How will we surmount this problem?

Here IEnumerator and IEnumerable can help us by providing a way to iterate over the object irrespective of the access specifiers of members.

Let us see how we can solve the problem using IEnumerator and IEnumerable interfaces.

Implementing IEnumerable and IEnumerator

First, I’m implementing the IEnumerable interface in our code.

The IEnumerable interface has one method named GetEnumerator (It should be implemented in our class).

using System;
using System.Collections;

namespace CSConsole
{
     class Items : IEnumerable
     {
         private string[] ItemList = new string[10];
         int pointer = 0;
         public void AddItem(string item)
         {
             ItemList[pointer++] = item;
         }
         public void RemoveItem(int index)
         {
             ItemList[index] = "";
         }
         public IEnumerator GetEnumerator()
         {
             return new ItemEnumerator();
         }

         class ItemEnumerator : IEnumerator
         {          
             
         }
     }
     
      class Program 
     {
         // Code removed for brevity
     }
}

In this code, the GetEnumerator method returns a child class that implements the IEnumerator interface.

The IEnumerator has two methods, MoveNext() and Reset(). Again, let us modify the code and implement these methods.

using System;
using System.Collections;

namespace CSConsole
{
     class Items : IEnumerable
     {
         private string[] ItemList = new string[10];
         int pointer = 0;
         public void AddItem(string item)
         {
             ItemList[pointer++] = item;
         }
         public void RemoveItem(int index)
         {
             ItemList[index] = "";
         }
         public IEnumerator GetEnumerator()
         {
             return new ItemEnumerator();
         }

         class ItemEnumerator : IEnumerator
         {  
             public object Current => throw new NotImplementedException();        
             public bool MoveNext()
             {
                 throw new NotImplementedException();
             }
             public void Reset()
             {
                 throw new NotImplementedException();
             }
         }
     }
     
      class Program 
     {
         // Code removed for brevity
     }
}

Explaining the code

MoveNext method: It returns true to indicate that we have not reached the end of the array and returns false to when we reach the last index of the array.

The Current object: It is used to return the current element in the array.

Reset method: It is used to reset the counter either after iterating till the last element of the array or to reset the iteration.

Implementing the logic

Finally, we are completing the code by implementing the logic for iterating. Here’s the complete code.

using System;
using System.Collections;
using System.Collections.Generic;

namespace CSConsole
{
    class Items:IEnumerable
    {
        private string[] ItemList = new string[10];
        int pointer = 0;
        public void AddItem(string item)
        {
            ItemList[pointer++] = item;
        }

        public void RemoveItem(int index)
        {
            ItemList[index] = "";
        }
        
        public IEnumerator GetEnumerator()
        {
            return new ItemEnumerator(ref ItemList);
        }

        class ItemEnumerator : IEnumerator
        {
            int index=0;
            string[] ItemList;
            public ItemEnumerator(ref string[] List)
            {
                ItemList = List;
            }

            public object Current
            {
                get{ return ItemList[index++]; }
            }
            
            public bool MoveNext()
            {
                return index>=ItemList.Length? false: true;
            }

            public void Reset()
            {
                index = 0;
            }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Items i = new Items();
            i.AddItem("Item 1");
            i.AddItem("Item 2");
            i.AddItem("Item 3");

            foreach (var items in i)
            {
                Console.WriteLine(items);
            }
            Console.ReadKey();
        }
    }
}

The index was used as a pointer that points at the current element of the ItemList array.

Points to remember

  • Both IEnumerable and IEnumerator are interfaces
  • Both interfaces are used for implementing enumeration.
  • IEnumerator allows read-only access to a collection.
  • IEnumerator has MoveNext and Reset methods and the current to get the current record.
  • IEnumerable defines one method GetEnumerator which returns an IEnumerator.

Wrapping it Up

In this post, you learned how to use IEnumerable and IEnumerator to iterate a class that has a collection.

0 Comments
Loading...