C# Power Programming #101–Method Invocation based on Runtime Type of Parameter

Virtual methods make it easy to invoke a specific method on an object based on the runtime type of the instance. However, invoking specific methods based on the runtime type of a parameter is not so easy. For this post we will use the following class hierarchy:

class Thing{}
class Spaceship : Thing {}
class Planet : Thing {}
class Sun : Thing {}
class Asteroid : Thing {}

The following code illustrates a trivial case of the issue:

void SomeMethod(Spaceship spaceship) {}
void SomeMethod(Thing thing) {}

Thing sample1 = new Spaceship();
SomeMethod(sample1);

The above code will always call the overload that takes a Thing because that is the declared type of sample1, even though it is obvious that the instance is a Spaceship. Of course this trivial example could easily be modified to declare sample1 as a Spaceship, but that would be cheating. Since the overload that takes a Thing will always be called, many developers resort to variations on the following pattern:</p.

void SomeMethod(Thing thing)
{
if (thing is Spaceship)
{
Spaceship spaceship = (Spaceship) thing;
SomeMethod(spaceship);
}
else if (thing is Planet)
{
Planet planet = (Planet)thing;
SomeMethod(planet);
}
// Repeat for each possible type…;”>}
}

This code smells bad in so many ways. There are more lines of code for plumbing than there are for actual work. There are many redundant casts. If a new derived class is created, this code must be manually tracked down and altered. There is a better was and it is known as Double Dispatch. This pattern has been known for at least 20 years, and there is documentation on it readily available on the web. The classic implementation is to create an interface to the code that has the method in question add one virtual method to the base class of the hierarchy which is overridden in each of the derived classes. The result will look something like the following:

interface IThingDispatchTarget
{
void Execute(Thing instance);
void Execute(Spaceship instance);
void Execute(Planet instance);
void Execute(Sun instance);
void Execute(Asteroid instance);
}

class Thing
{
public virtual void Dispatch(IThingDispatchTarget target)
{ target.Execute(this); }
}

class Spaceship : Thing
{
public virtual void Dispatch(IThingDispatchTarget target)
{ target.Execute(this); }
}

class Planet : Thing
{
public virtual void Dispatch(IThingDispatchTarget target)
{ target.Execute(this); }
}

class Sun : Thing
{
public virtual void Dispatch(IThingDispatchTarget target)
{ target.Execute(this); }
}

class Asteroid : Thing
{
public override void Dispatch(IThingDispatchTarget target)
{ target.Execute(this); }
}

public void SomeMethod(Thing thing)
{
thing.Dispatch(this);
}

void IThingDispatchTarget.Execute(Thing instance) { }
void IThingDispatchTarget.Execute(Spaceship instance) { }
void IThingDispatchTarget.Execute(Planet instance) { }
void IThingDispatchTarget.Execute(Sun instance) { }
void IThingDispatchTarget.Execute(Asteroid instance) { }

Ah, the sweet smell of success. We only need to create the interface and add the methods to the hierarchy once, regardless of how many places in the application there is the need to perform different processing based on the runtime type of the parameter. There are no casts anywhere in the code. There is no conditional plumbing in the class(es) that do the work. The code is also faster. Timing tests run on the above show that double dispatch has only 12.8% the overhead of the nested if; furthermore, the overhead of double dispatch is fixed regardless of the number of classes in the hierarchy, while the nested if with cases grows linearly with the number of types.

Conclusion

I can almost hear readers saying “This is a good technique, but it has been known for a long time….is it really worthy of the title Power Programming?”. The answer to that is no! This post is just setting the stage for discussing some of the limitations with this simple implementation. When we continue, we will cover some topics that I have rarely found published:

  • How do we extend this when there are additional parameters involved that must be passed?
  • How do we extend this when we want to dispatch to different targets based upon multiple parameters?
Advertisements

About David V. Corbin

President / Chief Architect Dynamic Concepts Development Corp. Microsoft MVP 2008-2011 (current Specialized in ALM) Microsoft ALM Ranger 2009-2011
This entry was posted in General Interest. Bookmark the permalink.

2 Responses to C# Power Programming #101–Method Invocation based on Runtime Type of Parameter

  1. The double dispatch and the visitor pattern are great if you have total control over both classes in the dispatch. If you don’t have total control of both classes and can’t implement the visitor pattern or double dispatch, here’s a little trick with the dynamic keyword that might come in handy: http://lynk.at/ghLVqk

    • @Peter – Thanks for bringing up dynamic and providing the link to your blog post. There are definately times where dynamic provides an excellent solution. In the next installment I will be discussing this is greater detail.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s