Using foreach with IEnumerator<T> / IAsyncEnumerator<T> in C# 9
The foreach
statement allows iterating 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 applies 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;
yield return tuple.Item3;
}
}
This is not the most awaited change in C#, but this is a nice new feature! Be sure to use it only when it makes sense to iterate of a type.
#Additional resources
- [Proposal] Extension GetEnumerator (VS 16.8, .NET 5)
- Discussion: Allow GetEnumerator method to be implemented via extension methods
- C# Language Design Meeting for March 23rd, 2020
- foreach, in (C# reference)
Do you have a question or a suggestion about this post? Contact me!