Method Invocation based on Runtime Type of Parameter (continued)

In my last post post, we reviewed the basics of the Double Dispatch pattern, and provided a simple example implementation. Unfortunately, this implementation is rather limited because it only handles the case where the instance in question in the sole parameter to the target method. Later, we will discuss ways to overcome this limitation, but first I want to expand on Peter Ritchie’s post on using the dynamic keyword that is available in .NET 4.0.

First, dynamic is a great tool and is an excellent choice for many situations where the goal is to have different methods execute based on the runtime type of a parameter. Slightly reworking his same to use our classes we have:

void DynamicMethod(dynamic thing)

{

    SomeMethod(thing);

}

Indeed, it is very “clean” to look at, no modifications were required to the class hierarchy, and in fact this approach will handle additional parameters and even dispatch based on multiple parameters with ease. Alas, as science fiction writer Robert Heinlein said (he did not originate the acronym but definitely popularized it, as did Larry Niven) TANSTAAFL  – “There ain’t no such thing as a free lunch.”.  There are in fact two side-effects of choosing to use dynamic:

  • Security – As Peter pointed out, there does not even have to be a relationship between the instance. Provided a target can be resolved, it will be used. In our case, this risk is minimal; but in the example Peter used with deserialization the risk can be much higher. Lets look at a sample very similar (but with significant difference) to Peter’s:

       IFormatter  formatter = new  BinaryFormatter ();
       dynamic shape = formatter.Deserialize(stream);
       shape.Execute();

If someone can hijack the stream (perhaps it is reading from a disk file), then the the object type can be completely replaced by altering the stream. Providing the object type has an execute method that takes no parameters, the code will run. Now, imagine if the de-serialized object is of type SelfDestructSequence instead of a sub-class of Shape.

  • Performance – Assuming we are constrained by a performance specification, the use of dynamic may have serious ramifications.  Measurements I have performed (using Stopwatch over 10 million executions and then averaging 10 runs after throwing out the fastest and slowest) show that dynamic is typically twice as slow as the original sequence of casts and compares, and 15 times as slow as the double dispatch used in the last post.

It is critical to put this performance measurement into context. If the overall operation is of significant time, or the number of calls through the dispatching mechanism is low, then the difference will be meaningless in the overall scheme of things. However, if the target method is very fast, and there are going to be large numbers of calls [such as in our later example where 10,000 instances are operated on in all possible combinations for a total of nearly 10 million calls] and there is a tight performance specification, then this must be taken into account.

With that out of the way, we can return to the original topics. First, lets look at scenarios where the target method requires one or more parameters in addition to the one that is being specially treated.  This is relatively simple, just change the signatures of the “IDispatchTarget” interface, along with the dispatch methods within the class hierarchy. But what should the new signatures be???

At first glance, it may appear easiest to modify the code to match the parameter types for the specific use-case. I recommend against this approach because it has a habit of growing out of control very quickly. If the parameters change, then the interface and class hierarchy will be impacted.  it gets even worse if the same class hierarchy needs to be dispatched to different methods in different classes. Either there will have to be multiple different interfaces (with a “Dispatch” method for each, or there will be multiple methods in the interface for the same object type, which usually leads to forced implementation of “NotSupported” methods.

Using generics helps the situation, but only a little. You still need different implementations for different numbers of parameters, and face the same issues as in the previous scenario.

What I have found to be the best solution for the majority of cases, is to use a single generic parameter, and pass it as a ref parameter. This gives rise to the following:

interface IThingDispatchTarget<T>

{

   void Execute(Thing instance, ref T data);

   void Execute(Spaceship instance, ref T data);

   void Execute(Planet instance, ref T data);

   void Execute(Sun instance, ref T data);

   void Execute(Asteroid instance, ref T data);

}

 

The reason for using pass by reference, is that it gives use the ability to efficiently package an transfer multiple items within the data parameter by using a struct, without the overhead of creating additional instances on the heap, or by copying the contents of the struct as would be required if it was pass by value. To complete the code sample..

class RealClass : IThingDispatchTarget<MyData>

{

    public void SomeMethod(Thing thing, ref MyData data)

    {

       thing.Dispatch(this, ref data);

    }

   void IThingDispatchTarget<MyData>.Execute(Thing instance, ref MyData data) { }

   void IThingDispatchTarget<MyData>.Execute(Spaceship instance, ref MyData data) { }

   void IThingDispatchTarget<MyData>.Execute(Planet instance, ref MyData data) { }

   void IThingDispatchTarget<MyData>.Execute(Sun instance, ref MyData data) { }

   void IThingDispatchTarget<MyData>.Execute(Asteroid instance, ref MyData data) { }

}

That wraps up handling additional parameters, leaving use with the topic of being able to dispatch based on the runtime type of multiple parameters. Since this post is getting a little on the long side, we will continue with it next time…

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 .NET Architecture & Implementation. Bookmark the permalink.

One Response to Method Invocation based on Runtime Type of Parameter (continued)

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