.Org Code for the social web, data management, and other interesting things.

5Aug/105

Generic Visitor Pattern in C#

The traditional model for application of the Visitor pattern, assuming that the visitable classes are immutable, is that the visitor class can produce data in one of two ways: it can either return data from the Visit() method, or it can aggregate data types inside the class. (The third option is to do both, of course.)

All well and good, but this model feels pretty limiting if you want to have multiple visitors that work on the same objects but produce different data types. In that case you can a) create as many versions of the visitor interface as you have data types to be returned, b) always aggregate within the visitor class, thus having to deal with mutable state inside the visitor, or c) always return object. Of those options, the first is tedious and the third is ugly. To my mind, both of them defeat the purpose of having the pattern in the first place. The second option is not bad, but I wanted something better than that.

Modern languages such as Java and C# include support for generics, so I tried my hand at writing a generic Visitor pattern that could be used on the same data structures by multiple visitor implementations that produce different data types. Using the new (well, newly old) features in C# 3.0, I was able to get a lot more. Let's start by looking at the IVisitor interface, which is parameterized by data type:

IVisitor is fairly straightforward when you think about it — we define a set of methods that accept an object of type R and produce an object of type R by visiting an IVisitable. This way we can write a single IVisitor interface for a data structure and then specialize it for a specific type later on, during the implementation. This is a nice improvement, but there's more we can do. C# 3.5 introduced extension methods, which allow us to attach methods to a wide range of classes at one time. Using a single extension method, we can eliminate the need for the second half of the traditional Visitor pattern, the Accept() method:

Now the Accept() method is available to be called on any class that implements IVisitable, as long as we provide an implementation of IVisitor. This means that we don't have to copy and paste the same code to every class that implements IVisitable any more! We can just call this method and .NET will wire it up.

The above definition makes use of a marker interface called IVisitable, but you could even restrict the application implicitly by which Visit() methods you define. In the case of a marker interface, you simply need to include a default method for visiting an IVisitable like so:

Which completes the generic visitor. Using a generalized class like this one allows us to program in a more functional and (hopefully) testable way. Plus, it's nice to actually use the new language features once in a while.

It's entirely possible that I've missed something in the implementation, so be sure to shout out in the comments if you've found a problem. Otherwise, enjoy your new generic Visitor in C#!

P.S. I should also give credit to some StackOverflow answers that suggested this approach, as well as a blog post on generic visitors in C# by Nicolas Penin, though I'm not aware of anyone else with an identical approach.

Update: Daniel Gröndal pointed out some issues with this approach in the comments. It turns out that the extension method does not work as I thought it did during my initial testing. Please read the comment thread for more details!