C# switch on type – you’re not my type!

April 6, 2010

OO purists have long argued that switching on the type of an object by using some conjured up member enum is wrong. And I agree. Fundamentally, it’s nasty and you can satisfyingly waggle your finger at poor OO design.

However, sometimes things aren’t so easy and straightforward. I’m currently working with a complex financial trade class hierarchy that simply can’t be changed – it just wouldn’t be practical. I needed some new functionality that switched on an instance of the base class of the hierarchy. Ok, so it breaks all the rules, but at least it’s switching on a common base type and not completely unrelated types. Peter Hallam’s article describes the problems with a generalised type switch built into the language and why it’s not practical.

So what follows is stream of software design consciousness that touches on the relative casting performance of C# as vs. is, lambda functions, generics and fluent interfaces (method chaining) that achieves a solution.

Lets assume we have some simple class hierarchy. These classes do nothing, but demonstrate the principles involved. We’ll use these throughout the examples.


// Our base class
public class Trade
{
    public void TradeMethod()
    {
    }
}

public class GenericTrade : Trade
{
    public void GenericTradeMethod()
    {
    }
}

public class SwapTrade : Trade
{
    public void SwapTradeMethod()
    {
    }
}

public class SpotTrade : Trade
{
    public void SpotTradeMethod()
    {
    }
}

A first stab at switching on type might be like this:


public void doSomething(Trade trade)
{
    if (trade is GenericTrade)
    {
        GenericTrade genericTrade = trade as GenericTrade;
        genericTrade.GenericTradeMethod();
        // ....
        // ....
    }
    else
        if (trade is SwapTrade)
        {
            SwapTrade swapTrade = trade as SwapTrade;
            swapTrade.SwapTradeMethod();
            // ....
            // ....
        }
        else
            if (trade is SpotTrade)
            {
                SpotTrade spotTrade = trade as SpotTrade;
                spotTrade.SpotTradeMethod();
                // ....
                // ....
            }
            else
            {
                // Default actions
                // ....
                // ....
            }
}

Well it works, but it has a couple of problems.

  • C# offers the syntactically concise way to upcast and downcast using as and is. Unfortunately as Emilio Guijarro shows, the relative performance of ‘as’ vs ‘is’ when typecasting means ‘is‘ is pretty slow.
  • Somehow, the if…else.. If …else structure seams very clunky when there’s more than a few types.

To get around using the slow ‘is‘ is easy.


public void doSomethingElse(Trade trade)
{
    GenericTrade genericTrade = new GenericTrade();
    SwapTrade swapTrade = new SwapTrade();
    SpotTrade spotTrade = new SpotTrade();

    if (genericTrade as GenericTrade != null)
    {
        genericTrade.GenericTradeMethod();
        // ....
        // ....
    }
    else
        if (swapTrade as SwapTrade != null)
        {
            swapTrade.SwapTradeMethod();
            // ....
            // ....
        }
        else
            if (genericTrade as GenericTrade != null)
            {
                genericTrade.GenericTradeMethod();
                // ....
                // ....
            }
            else
            {
                // Default actions
                // ....
                // ....
            }
}

This requires an instance of each type, but the structure is even more horrible than before. So abandon that way of doing it. What we really need is something that is readable. Something like this would be ideal:


public void doSomething(Trade trade)
{
    switch (trade)
   {
      case GenericTrade:
            // ...
            // ...
            break;
      case SwapTrade:
            // ...
            // ...
            break;
       case SpotTrade:
            // ...
            // ...
            break;
        default:
            // ...
            // ...
            break;
      }
}

Bart De Swet’s article that describes a solution that yields a similar code structure. This is implemented using method extensions which is unnecessarily complicated, so I devised a similar solution using instance methods.

So here’s my take on a switch class – I’ve called it SwitchOnType for clarity:


public class SwitchOnType<T1>
{
    bool _break = false;
    private T1 _object;

    public SwitchOnType(T1 obj)
    {
        _object = obj;
    }

    public SwitchOnType<T1> Case<T2>(Action<T2> action) where T2 : class
    {
        if (_break == false)
        {
            T2 t = _object as T2;
            if (t != null)
            {
                action(t);
                _break = true;
            }
        }
        return this as SwitchOnType<T1>;
    }
    public void Default<T2>(Action<T2> action) where T2 : class
    {
        if (_break == false)
        {
            T2 t = _object as T2;
            if (t != null)
                action(t);
        }
    }
}

And here’s how it’s used with our class hierarchy example:


public void doSomething(Trade trade)
{
    new SwitchOnType<Trade>(trade)
        .Case<GenericTrade>(genericTrade =>
        {
            // It's a GenericTrade
            genericTrade.GenericTradeMethod();
            // ...
            // ...
        })
        .Case<SwapTrade>(swapTrade =>
        {
            // It's a SwapTrade
            swapTrade.SwapTradeMethod();
            // ...
            // ...
        })
        .Case<SpotTrade>(spotTrade =>
        {
            // It's a SpotTrade
            spotTrade.SpotTradeMethod();
            // ...
            // ...
        })
        .Default<Trade>(t =>
        {
            // Default actions
            t.TradeMethod();
        });
}

Although there’s a few extra {‘s and )’s, the structure is close to the ideal. This is mainly possible using method chaining (also known as fluent interfaces), by returning this (well a genericized this) in the case method and passing the code to be executed if the case test succeeds as a lambda function. Note if none of the Case methods succeeds, the Default method will get called. If you want to know more about fluent interfaces read Martin Fowler’s article.

So just accept that sometimes you have no choice and have to accept that it’s right to do what’s wrong – is that software or life. Or both.

Advertisements

One Response to “C# switch on type – you’re not my type!”


  1. My relatives always say that I am wasting my time here at web, however
    I know I am getting know-how every day by reading such pleasant articles.


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

%d bloggers like this: