Simple typeswitch in C# 3.0, Part 1: The Problem
September 12th, 2007
This is the first post in a two-post series on a typeswitch implementation in C#.
This one contains a problem statement and possible solutions in other languages.
The second one will contain a Switch.Type description and benchmarks.
Somewhat often I find myself writing code to do something based on runtime type of a value.
A classic case is to filter a tree with different types of nodes (expression tree, for example).
The code often looks like
if (x is A) { DoWithA(x as A); } else if (x is B) { DoWithB(x as B); } else // ...
Or, if you are a heavy performance freak like me it is like
A a = x as A; if (a != null) { DoWithA(a); return; } B b = x as B; if (b != null) { DoWithB(b); return; } // ...
After second type it really starts to smell.
I could have used a Visitor.
But I really dislike it due to the coupling between the Visitor interface and the underlying class hierarchy.
Also requires me to extend hierarchy with a zero value Accept method.
I can also use some kind of hashtable-based smart resolver, but it would be complex and slow.
Actually, that is not an obscure problem and a lot of other languages have their solutions.
There are two common ones:
-
OO concept known as multiple dispatch.
Multiple dispatch is just a bunch of “method overloads” resolved by runtime environment basing on the runtime argument types.
This is quite different from ordinary method overloading — for example, in C# compiler picks an overloaded method during compilation.Actually, .Net has a way to do multiple dispatch through Reflection (Type.InvokeMethod), but it quite slow and not compiler-type-safe.
There is a brilliant paper “Generalized Interfaces for Java” that gives some insight on useful multiple dispatch in Java/C#-like languages.
Hopefully we’ll get that functionality in C# and CLR sooner or later. - Functional language concept known as pattern matching.
This is a kind of powerful switch/case statement (with a simplified syntax).
I do not actually know much about functional languages, so that is my understanding.
The simplest possible construct (I do not want to dive into multiple dispatch) might look like this:
typeswitch (x) { case (A a): DoWithA(a); break; case (B b): DoWithB(b); break; default: throw new ArgumentException(); }
And lcs Research C# Compiler has a similar syntax sample:
typeswitch (o) { case Int32 (x): Console.WriteLine(x); break; case Symbol (s): Symbols.Add(s); break; case Segment: popSegment(); break; default: throw new ArgumentException(); }
Cω language also had an actual typeswitch construct.
But I was not able to find out it’s syntax (my old VS.Net is somewhy ruined and web is silent on it).
Anyway, Andrey Titov (who I hope will also blog someday) reminded me that it compiled to zero IL (seems it was too experimental).
Stay tuned, next time we’ll see how it is possible to emulate typeswitch in C# 3.0.