is as typeof double dispatch or forgotten craftsmanship

Using modern programming languages such as C# offers you a large variety of reflection methods. Amongst both rookie and veteran developers I am observing more and more use of little helpers like the ‘is’, ‘as’ or ‘typeof’ operators. This is no wonder as it is so easy and convenient to use them. For instance a common recognizable pattern is the method of taking a certain base class as the parameter and performing various actions. Such actions depend on the actual instance determined by the mentioned operators. The following code illustrates this pattern:

public void Dispatch(BaseClass baseClass)
{
    if (baseClass is DerivedClass1)
    {
        DoActionOnDerivedClass1((DerivedClass1)baseClass);
    }
    else if (baseClass is DerivedClass2)
    {
        DoActionOnDerivedClass2((DerivedClass2)baseClass);
    }
...
}

Besides using the “is” operator one might also use an “as” construct, like:

var derivedClass1 = baseClass as DerivedClass1;
if (derivedClass1 != null)
{
    DoActionOnDerivedClass1(derivedClass1);
    return; // or else if
}

Also, it is also possible to compare the types via .GetType() and typeof(…) or to boost things a little by accessing the TypeHandle property.

While this technique looks nice and easy to read, it is indispensable to have a look at the impact it has on the surrounding architecture as well as performance and consider possible alternatives before setting it in place.

From a design point of view there are the following observations:

– The dispatching code is kept in one place and can become rather big when there are a lot of derived classes
– It is not enforced that more if-clauses are added when more derived classes are introduced
– The ‘DoAction’ method calls are hard coded. It will be difficult to reuse the Dispatch method for other purposes

Introducing a virtual method in BaseClass (called for instance ‘DoAction’) will invalidate the points above very quickly. Depending on the actual instance type the desired action will be executed. But wait a second: The whole point of the example is about performing actions (e.g. data manipulations) outside the class; likely actions which have not even been known to the author of the dervied classes at the time of writing.

Fortunately there is a software pattern for this problem, called ‘Visitor pattern’ or ‘double dispatch’. Without explaining the whole pattern here (this has been done many times on the internet) the following can be said as a quick recap: The base class as abstract and all derived classes as implementation have a virtual method called ‘Accept’ with a ‘Visitor’ class as parameter. In this method every derived class calls a ‘Visit’ method of the visitor class passing itself as reference. The visitor class provides a distinct ‘Visit’ method for every derived class and can perform previously mentioned actions in those methods.

Keeping this pattern in mind the previous list of observations changes a little:

– The dispatch code is distributed
– It is impossible to forget a derived class in this concept: Derived classes must implement method ‘Accept’
– By having an abstract visitor implementation it is easy to derive from it and provide a different set of actions

It becomes clear that double dispatch pattern is by far superior to the use of ‘is’,’as’ or ‘typeof’ from a design point of view.

But how do things look from the performance side?

People who are familiar with C++ might look at all those reflection possiblities with a little skepticism (having RTTI in their mind and the ‘rule’ to use it as seldom as possible). They are aware of what is how expensive and this is something I am missing a bit in the world of C# development. Reflection is expensive and should not be used in code which is executed with a high frequency.

But enough theory: I have written a small performance measurement application to find out which dispatching method takes how long. There are 4 candidates: is, as, typeof and dd (double dispatch).

The program has a base class and 3 derived classes. After start it creates a list of base classes with 50.000.000 random derived class entries and runs the 4 dispatcher types on the list.

On my machine (dual core from 2007) one run (release mode) leads to the following results:

TypeofDispatcher: 871ms
IsDispatcher: 618ms
AsDispatcher: 610ms
DoubleDispatcher: 440ms

The double dispatcher gives a result almost 40% faster than the candidate on the 2nd place, the ‘as’ operator.

It becomes increasingly clear that the visitor pattern is a double winner here. On the one hand it offers cleaner designer, enforces handling of all derived types and is easily extensible for different sets of actions. On the other hand it outperforms its reflection competitors significantly. Looking closer and digging deeper into why this is so is a topic for a future blog entry but for now the take home message is: Avoid usage of ‘typeof’, ‘as’, ‘is’ operators whenever possible.

Flattr this!

Leave a Reply

Your email address will not be published. Required fields are marked *