Using foreach with IEnumerator<T> / IAsyncEnumerator<T> in C# 9

  • Gérald Barré

The foreach statement allows to iterate on a collection. The foreach statement isn't limited to collection or types that implement IEnumerable<T>. As explained in a previous post, you can use it with an instance of any type that has the public parameterless GetEnumerator method whose return a type having the public Current property and the public parameterless MoveNext method whose return type is Boolean.

In C# 9, the GetEnumerator method can be provided using an extension method! For instance the IEnumerator<T> is not compatible with the foreach statement as it doesn't have a method named GetEnumerator. You can create an extension method named GetEnumerator which make it possible 😃

Before using this feature, you need to update the language version to 9.0 or preview:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
    <LangVersion>9.0</LangVersion>
  </PropertyGroup>
</Project>

Then, you can create the extension method and use it:

static class Extensions
{
    public static IEnumerator<T> GetEnumerator<T>(this IEnumerator<T> enumerator) => enumerator;
}

class Program
{
    void Main()
    {
        IEnumerator<int> enumerator = Enumerable.Range(0, 10).GetEnumerator();

        // Does not compile in C# 8
        // Possible with C# 9 and the extension method
        foreach (var item in enumerator)
        {
            Console.WriteLine(item);
        }
    }
}

The same apply for await foreach. You can now use an extension method to provide the GetAsyncEnumerator method:

static class Extensions
{
    public static IAsyncEnumerator<T> GetAsyncEnumerator<T>(this IAsyncEnumerator<T> enumerator) => enumerator;
}

class Program
{
    static async Task Main()
    {
        IAsyncEnumerator<int> enumerator = GetAsyncEnumerator();
        await foreach (var item in enumerator)
        {
            Console.WriteLine(item);
        }
    }

    static async IAsyncEnumerator<int> GetAsyncEnumerator()
    {
        yield return 0;
        await Task.Delay(1);
        yield return 1;
    }
}

Last but not least, you can create extension methods for any type. For instance, you could iterate on ValueTuple using a foreach by creating the following extension method:

class Program
{
    static void Main()
    {
        foreach (var item in (1, 2, 3))
        {
            Console.WriteLine(item);
        }
    }
}

static class Extensions
{
    public static IEnumerator<object> GetEnumerator<T1, T2, T3>(this ValueTuple<T1, T2, T3> tuple)
    {
        yield return tuple.Item1;
        yield return tuple.Item2;
    }
}

This is not the most awaited change in C#, but this is a nice new feature! Be sure to use it only when it really makes sense to iterate of a type.

#Additional resources

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?Buy Me A Coffee