How to implement IEnumerable in C#

When you create your own class, you may want an easy way to cycle though the data within the class.

Ever wondered how the ‘foreach’ works?

foreach (double val in MyClass)
{
    //do something with val
}

Something like this is only possible if we tell MyClass what values within MyClass we want to cycle through.

This is possible by implementing IEnumerable.

An example

Let’s say MyClass looks like this:

using System.Collections.Generic;

namespace MyNameSpace
{
    public class MyClass
    {
        public MyClass()
        {
            myData = new List<double>(new double[] { 3.4, 1.2, 6.2 });
        }

        private List<double> myData;
    }
}

What if we create an instance of MyClass, and want to run a foreach loop on it?

What we’d like is for the foreach to loop through the three values contained in myData.

But it doesn’t work yet. What’s missing?

error enumerator

The error reads: foreach statement cannot operate on variables of type ‘MyNamespace.MyClass’ because ‘MyNamespace.MyClass’ does not contain a public definition for ‘GetEnumerator’.

So how do we add the definition for GetEnumerator?

How to implement IEnumerable in C#

We do this by implementing the IEnumerator interface to our class.

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

namespace MyNamespace
{
    public class MyClass : IEnumerable
    {
        public MyClass()
        {
            myData = new List<double>(new double[] { 3.4, 1.2, 6.2 });
        }

        private List<double> myData;

        public IEnumerator GetEnumerator()
        {
            foreach(double val in myData)
            {
                yield return val;
            }
        }
    }
}

We have done three things:

  1. Added using System.Collections;
  2. Added : IEnumerable to let the compiler know that there’s something in the class we can iterate through
  3. Added method GetEnumerator(). This is what is called when we try to iterate through an instance of the class.

Note the use of the special keyword yield. This allows us to return an enumerated value without breaking from the GetEnumerator method.

So, how does this look now?

working-enumerator

Perfect!

Specify a return type with IEnumerable

Notice how we never specified anywhere that the enumerator would be returning a double?

By default, the enumerator returns an object. When we run our foreach loop, it is clever enough to implicitly cast each enumerated output as a double, since we specified that we wanted each item to be of type double.

What if another programmer, using our class, doesn’t know to expect a double, and they just use a var (or something else)?

We get an object instead – and an error.

enum-error-var

Can we tell GetEnumerator which data type it should be returning?

In your class, implement IEnumerable<T> instead. Then, there are two methods (not one) to implement.

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

namespace MyNamespace
{
    public class MyClass : IEnumerable<double> //Specify your return type in the angle brackets.
    {
        public MyClass()
        {
            myData = new List<double>(new double[] { 3.4, 1.2, 6.2 });
        }

        private List<double> myData;

        //Put your enumerator code in this method. Specify your return type in the angle brackets again here.
        public IEnumerator<double> GetEnumerator()
        {
            foreach(double val in myData)
            {
                yield return val;
            }
        }

        //This method is also needed, but usually you don't need to change it from this.
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
}

Now, we should find that, even if we type ‘var’, Visual Studio knows what type to expect.

var-enum-working

This is the way that I would recommend. Specify your data types – it will help reduce bugs and mistakes later on in your project.

Add a Comment

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

Time limit is exhausted. Please reload CAPTCHA.