C# 9 and Foreach Extensibility

This post is a part of C# Advent Calendar 2020. It’s not intended to be read very seriously.

The list of new features in C# 9 is long. It will take years for people to get used to all the new constructions.

Let me start the blogpost with a paragraph from the end of the What’s New:

“In addition, the foreach loop will recognize and use an extension method GetEnumerator that otherwise satisfies the foreach pattern. This change means foreach is consistent with other pattern-based constructions such as the async pattern, and pattern-based deconstruction. In practice, this change means you can add foreach support to any type. You should limit its use to when enumerating an object makes sense in your design.”

You see, warnings like these (“you should limit…”) always capture my attention. What is the feature about and what are the language designers afraid of?

foreach is a strange beast: it’s open for extension (for making your own classes iterable in the way meaningful for your problem domain) in a somewhat surprising way: it’s not that if your class implements IEnumerable, foreach will work with it. It’s that if your class implements certain methods, foreach will work with it, regardless of the fact that the class is marked by any interface.

As far as I know, the original rationale for the mechanism is the fact that you might want to specify what exact type you want to return from enumeration and that IEnumerable can only speak about object because… generics were not yet a thing in C# 1.0.

This special behavior has a nice potential for interaction with extension methods. C# does not have type classes: you cannot extend the set of interfaces a class supports from outside. But you can extend the set of methods using the extension method mechanism. If there is a library class you don’t own that does not enumerate on itself and you would like to enumerate it, you should actually be able to add “foreach support” from outside because you don’t need to add an interface, you just need to add an (extension) method.

The new behavior of C# 9.0 is that this theoretical construction now works in practice.

Misuse

Now, everyone can imagine how to leverage that potential for good. Let’s not do that.

Von Neumann Integers

Everyone and their dog talk about application of category theory into programming. Applicatives! Monoids! Functors! I ask, why shouldn’t we start with introducing set theory into our everyday programming practices? How come that we cannot iterate over integers to reveal their true set-theoretic nature?

Well, now we can:

using System;
using System.Collections.Generic;
					
public static class VonNeumannExtensions
{
	public static IEnumerator<int> GetEnumerator(this int number)
	{
		for (var i = 0; i < number; i++)
		{
			yield return i;	
		}
	}
}

public class Program
{
	public static void Main()
	{
		foreach (var i in 5)
		{
			Console.WriteLine(i);
		}
	}
}

The output before C# 9 is a compilation error:

foreach statement cannot operate on variables of type 'int' because 'int' does not contain a public definition for 'GetEnumerator'

The output from C# 9 on:

0
1
2
3
4

JavaScript Objects

JavaScript is such a great programming language! Let’s get closer to its shining glory by allowing iteration over any object:

using System;
using System.Collections.Generic;
					
public static class JavaScriptExtensions
{
	public static IEnumerator<string> GetEnumerator(this object js)
	{
		var members = js.GetType().GetMembers();
		for (var i = 0; i < members.Length; i++)
		{
			yield return members[i].Name;	
		}
	}
}

public class Program
{
	public static void Main()
	{
		foreach (var i in new Object())
		{
			Console.WriteLine(i);
		}
	}
}

The output from C# 9 compiler:

GetType
ToString
Equals
Equals
ReferenceEquals
GetHashCode
.ctor

Conclusion

We really should limit use of the new feature when it makes sense in our design. We shouldn’t make integers enumerable, we shouldn’t make all objects enumerable.

But, we should know why the technology we use behaves the way it does. Trying silly things is a way to learn.

» Subscribe to RSS
» Write me an e-mail