Simplifying the visitor pattern with the dynamic keyword

Posted on February 29, 2012

0


The visitor pattern can be a very useful tool to have to hand when wanting to apply type specific processing to a collection or graph of varying subtypes. I suspect anyone that works with event sourcing or event streams of any kind (logging etc) would most likely have implemented the visitor pattern at some point or another. I have also seen it well implemented in accounting systems applying functions over different transaction types.

The visitor pattern isn’t, however, the easiest or conceptually most straight forward pattern in the book. It can make you feel a bit like the main character in the computer game Portal, jumping into a method to eventually pop up somewhere completely different. With a portal gun in your hand. And funny shoes.

Here is a very basic example. A list of ICharacter is iterated over, passing in the DrSeussVisitor intance to each ICharacter‘s Accept method. Each implementing ICharacter type passes this to the visitor’s Visit method and thus allows the compiler to select the correct DrSeussVisitor method overload which is pretty much what the visitor pattern is all about.

void Main()
{
    var characters = new List { new CatInTheHat(), new TheFish() };
	
	var visitor = new DrSeussVisitor();
	
	foreach (var character in characters)
	{
		character.Accept(visitor);
	}
}

class DrSeussVisitor
{
	public void Visit(CatInTheHat character)
	{
		Console.WriteLine("The cat in the hat visited");
	}
	
	public void Visit(TheFish character)
	{
		Console.WriteLine("The Fish visited");
	}
}

interface ICharacter
{
	void Accept(DrSeussVisitor visitor);
}

class CatInTheHat : ICharacter
{
	public void Accept(DrSeussVisitor visitor)
	{
		visitor.Visit(this);
	}
}

class TheFish : ICharacter
{
	public void Accept(DrSeussVisitor visitor)
	{
		visitor.Visit(this);
	}
}

Note: The DrSeussVisitor would normally have an interface IDrSeussVisitor so that you can reuse the plumbing for different types of visitors but I have omitted it for brevity.

The Dynamic Language Runtime.

I have to admit I haven’t really utilised the Dynamic Language Runtime (the dynamic keyword in C#) enough in the past. My experience is limited to spiking out a solution using the ExpandoObject to create a dynamic datasource from xml for the Aspose word merge library. It worked pretty well and I was pleased with the end result but it never went into production code. The ExpandoObject probably could warrant another blog post about this at some point.

Anyone wanting to get a good introduction to the DLR and the dynamic keyword would be advised to read the chapter in Jon Skeet’s C# in Depth (2nd Edition) book.

So I have said we can simplify the visitor pattern using the dynamic keyword but how? Very easily it turns out. The key thing is that instead of relying on double dispatch to place the visitor in the appropriate context for the correct method overload selection to take place we instead pass a dynamic object to the visitor’s Visit method(s). Simply assign the ICharacter in the foreach loop to a dynamic temporary variable and pass this variable into the visitor (here we would previously have passed the visitor instance to the character’s Accept method). Now you can remove a lot of the boiler plate code such as the Accept method, in fact you can remove the ICharacter interface all together and just have a collection of object, if you so wish.

The resulting code is quite a bit smaller and in my view, a fair bit easier to read.

void Main()
{
	var characters = new List<object> { new CatInTheHat(), new TheFish() };
	
	var visitor = new DrSeussVisitor();
	
	foreach (var character in characters)
	{
		dynamic c = character;
		visitor.Visit(c);
	}
}

class DrSeussVisitor
{
	public void Visit(CatInTheHat character)
	{
		Console.WriteLine("The cat in the hat visited");
	}
	
	public void Visit(TheFish character)
	{
		Console.WriteLine("The Fish visited");
	}
}

class CatInTheHat {}
class TheFish {}

How does it all work?

As mentioned above the visitor pattern is about enabling the compiler to select the correct method overload on the visitor so that the appropriate processing for the particular type can be applied. I am not going to go into a lot of details into how the DLR works internally, partially because I don’t know it well enough and partially as it is out of scope for this blog post, but it goes something like this:

As soon as anything dynamic is involved in a method, in this instance we are passing it the now dynamic ICharacter reference to the Visit method, the responsibility for the method call binding is assigned to the DLR and not the compiler. The DLR will use the actual runtime type of a participating object and is thus able to bind the correct overload. The end result is that the dynamic overload resolution has a similar effect to our visitor pattern double-dispatch boilerplate. The appropriate method overload is selected, which is what we want.

The visitor pattern with the dynamic keyword results in a flatter, more intuitive code structure, something that I for one appreciate. Arguably it may not be the visitor pattern anymore as Gamma et. al. specified it, however it fills the same space and gets the same job done. Anything that removes boilerplate code and puts the focus back on the business functionality must be a good thing yes? Ok, nice to hear we agree.

It is worth mentioning that whenever you introduce the dynamic keyword into your code base it is essential to have good unit tests coverage of the target code section to ensure that no avoidable runtime exceptions are thrown.

Oh and both examples are runnable in LINQPad, of course.

Laters.

Advertisements
Tagged: , ,
Posted in: Patterns