Monday, 3 November 2008

I am sure that most programmers managed to learn some specific design patterns before they even knew what design patterns were. This is certainly true for myself – and while I cannot say what my first design pattern was, I can tell you about the first one I remember learning: the Visitor pattern. I was midway through a university class on object oriented programming and in my spare time I was writing a turn based puzzle game in C# that involved daggers and goblins and dragons and doors and keys and whatnot. Now to make the thing work, I was doing something that felt very wrong. I was writing lots of switch statements that described the interactions between the different objects. I experimented, looking for better solutions using overloading and overriding, but none of it seemed to work. I asked my lecturer for help, and of course this is where I learned about visitor. Initially I was elated, but in time my opinion changed: visitor is this ugly bloated thing, designed to get around the lack of double dispatch support in most object oriented languages. Its a hack, a workaround. As with arguably all design patterns, its a trick to deal with limitations in the language.

So today I was watching this video that goes into more detail on the new C#4 features and something was said about runtime overload resolution that flicked a switch in my brain and suddenly I realised a fact that I am sure was mindblowingly obvious to people much smarter than I: the C#4 dynamic keyword supports double dispatch. If you are prepared to take the performance hit and just want to get something up and running, you can forget the visitor pattern. Lets look at an example based on the sort of thing I was trying to do years ago with my little game:

Clearly, it does not work as intended. The code I’ve written makes some sort of sense, but its not correct. If you look at the Introduce method, you can see that it takes two entities. As a result, the call to GetGreeting will be resolved at compile time to Entity.GetGreeting(Entity e) and no polymorphism will occur. You cannot fix the problem by simply making the methods virtual and overrides, as the overriden versions take different arguments. Redeclaring the GetGreeting methods using the new operator also does not help. If you are facing this problem with C# 3 or earlier, its time to apply the visitor pattern. I won’t do that here, there are plenty of examples of Visitor on the web.

But if you are using C#4, its time for dynamic to come to the rescue. Here is a slightly different implementation of an Introducer:

The ONLY thing I have changed is the signature. The parameters are now typed as dynamic rather than Entity. I plug this introducer in and this is the output:

BobFoo is introduced to JohnFoo by the DynamicIntroducer...
BobFoo says: Hi there JohnFoo! Beware, there may be evil Bars nearby!
JohnFoo says: Hi there BobFoo! Beware, there may be evil Bars nearby!
BobFoo is introduced to JerryBar by the DynamicIntroducer...
BobFoo says: Curse you! I hate all Bars!
JerryBar says: The Foos are a scourge on this planet! Take this!
JerryBar is introduced to SamBar by the DynamicIntroducer...
JerryBar says: Greetings SamBar! Have you seen any evil Foos lately?
SamBar says: Greetings JerryBar! Have you seen any evil Foos lately?

It works! How? Well, once you are using the dynamic type, overload resolution occurs at runtime rather than compile time, and is done using the runtime types of the arguments rather than the compile time types. My nice, succinct code that describes the different interactions now works just how I intended. But at what cost? Putting the possible performance problems aside, we have made the code more fragile. If someone passes a non-Entity to the Introduce method, we’ll get a runtime error! Well this is actually a trivial fix that only occurred to me while writing this post:

The only method call that actually needs to be dynamic is GetGreeting, so we can change the method signature back to as it was before and just introduce a cast to dynamic for the GetGreeting invocation. Much better. There is one more thing I would like to try to do: hide the need for GetGreeting to be invoked dynamically from the caller. The crude way is to use a static method, but I’m not a fan of that. Tomorrow I will see if there is an elegant way to approach it.

So about two months ago I responded to a post on Jimmy Bogard's blog. Jimmy was lamenting wasting a bunch of time by accidently calling a library method with his arguments reversed (an annoyance that I am sure most programmers are familiar with) and I wrote the following:

This is a good example of why I wish C# supported named parameters rather than ordered parameters. Regex.IsMatch(pattern: @"^\d*$", input: "123456" ) Is so much clearer.

The cool thing is that I just pasted my line of code directly into a C#4 console application (I’m using the VS2010 CTP) and it compiled first time! I'm a fan of named parameters, simply because they make code more readable. If I working with some existing code, I want to understand what it is doing as quickly as possible, and using named parameters will help that cause.

Lack of named parameters in the past has caused the BCL team to create types such as System.IO.SearchOption - an enum with two values: AllDirectories and TopDirectoryOnly. Now let me be clear, I am not at all saying that types like this one are a bad idea. The problem, pre C#4, is that the more readable solution involved pulling your thumb out and writing more code, such as this enum. The BCL programmers could have been lazy and just made the Directory.GetDirectories() method accept a boolean 'recursive' parameter, but fortunately for us they weren't lazy and so they created an enum to make for more readable code. The good news is that with C#4, programmers such as myself can be lazy and leave the method signature using a boolean, safe in the knowledge that the caller can make a readable call by using a named parameter.

The downside to this approach is that rather than FORCING the caller to write readable code, you are HOPING they will elect to write the more readable option. So what is better? Well this is touching on a much bigger subject, the whole issue of how much trust you should place on the individual developer. I can’t give you a definitive answer for that, it depends on the environment and developer culture. Perhaps it would suffice to say that I would prefer to work in an environment where I am safe to force rarely, and hope often.