Search code examples
c#design-patternsprogramming-languagesvisitor-patternmultimethod

Static (compile time/strictly typed) multi-dispatch support in C#


I recently found myself using the same pattern over and over in my code. Basically it is a variation of visitor pattern that I use to resolve references to instances of base classes to derived ones. This approach requires lots of boilerplate code.

The questions:

  • How do I multi-dispatch a method in C# in a static/strictly-typed manner without writing tons of visitors code?
  • Is there a tool/extension that generates this code?
  • Why there is nothing in C# language that addresses multi-dispatching? I don't believe I am the only one who finds this annoying. I might be awfully wrong and the problem doesn't exists, so I would like to know how you go along with it.

Solution

  • How to multi-dispatch a method in C# in a static/strictly-typed way without writing tons of visitors code?

    I'm not aware of any technique for that.

    In the Roslyn version of the C# compiler, which is written in C#, we use the visitor pattern all over the place, on type hierarchies that have dozens or hundreds of members. We have written a utility that converts an XML description of the types into the declarations of the types along with base classes for the visitors. That seems to work quite well for us.

    Is there a tool/extension that generates this code?

    There is; we wrote it ourselves. It wasn't hard. You can do the same.

    Why doesn't C#, as a language, have anything to address multi-dispatch issue?

    We have a list of possible language features literally longer than your arm. Any given release we have the budget to do two or three of them, tops, and therefore we concentrate on getting the most possible bang for the buck.

    Making it easier to implement double (or multiple) virtual dispatch via auto-generating the visitor pattern has never made it anywhere near to the top of that list. There are literally dozens of other possible patterns that we could be embedding in the language that are more "bang for the buck". If you do a survey of popular languages you will find that very few languages support double or multiple virtual dispatch via static analysis, and the ones that do are not hugely popular. There's a reason for that: first, because it's not actually a very useful feature, and second, because when you do need it, you can achieve it by implementing the pattern yourself, or by using dynamic dispatch.

    As you note, there are significant drawbacks to both the pattern-based approach and the dynamic dispatch approach. But, though there are drawbacks, they are both feasible for ordinary line-of-business developers to implement themselves should they need to. When evaluating which patterns to embed in the language, we lean towards those patterns that are really quite difficult for ordinary developers to implement themselves using a pattern-based approach. The visitor pattern is not difficult; it's just verbose.

    For example: In C# 2 we chose to embed the "sequence generator" pattern in the language. In C# 3 we chose to embed the "query comprehensions with sequence monads" pattern in the language. In C# 4 we chose to embed the "dynamic dispatch" pattern in the language. In C# 5 we chose to embed the "pass the current continuation as a delegate" pattern into the language. All of these are examples of big "bang for buck" -- they were expensive features to implement, but they fundamentally make new styles of programming available in the core language.

    There's only a finite amount of effort available in any release; embedding one pattern in the language in a release prevents us from embedding any other pattern in the language in that release because there simply isn't the budget to do it.

    When embedding the "double (or multiple) virtual dispatch" pattern into the language becomes the best possible way to spend our budget, we'll do it, and not before. You should therefore expect a long wait.