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:
csproj (MSBuild project file)
<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:
C#
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:
C#
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:
C#
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
Do you have a question or a suggestion about this post? Contact me!