Reducing Code Complexity on Switch-blocks

Using a Dictionary instead of a switch-case construction to reduce complexity and increase testability

Introduction

A switch-block becomes complex very easily. All code is placed in one method and parts of it are used multiple times. In a lot of cases, a switch-block is based on an enum. Next to that, it's not possible to use a fall-through in C#. The only way to go from case to case is the use of the goto statement, which isn't a good idea when reducing complexity. Although I've written the examples in C#, it wouldn't be hard to apply the principles to other languages.

A Solution

A nice solution to get rid of large switch constructions is the use of a Dictionary<enum,delegate>. This way every element of the enum can be attached to a method. It is possible to call the right method for every enum possible. Because every enum has its own method, it's very easy to use this construction in a testdriven environment. Another nice thing is that it's no problem to call one method from another. Let me explain a little further with an example and some pseudo code. Imagine a program that prepares food. This part contains the recipes.

Let us start with the following enum:

Enum food{
Apple,
ApplePie,
AppleJuice,
Pizza
}

It's not hard to imagine that all of the foods need a specific preparation, but that some actions need to be done for different foods, like peeling an apple or baking in the oven.

To add the preparations to the Dictionary<enum, delegate>, we first need to define a delegate method:

delegatebool Preperation();

Now we need to define the actual preparation methods for every item of the enum, making sure they're declared the same way as the delegate, thus the same parameters and return value. The method returns a boolean when preparation is successful.

Awards / Honers • October 2010,2011,2012,2013: Awarded Microsoft Expression Blend MVP• June 2009: Second Place in the WinPHP challenge• February 2009: Runner-up in de Mix09 10k Challenge • June 2008: Winner of the Microsoft expression development contest at www.dekickoff.nl

BioI started programming around 1992, when my father had bought our first home computer. I used GWBasic at that time. After using QBasic and Pascal for a few years I started to learn C/C++ in 1996. I went to the ICT Academy in 1997 and finnished it in 2002. Until December 2007 I worked as a 3D specialist. Besides modelling I worked on different development projects like a 3D based Scheduler and different simultion tools in C# and Java. Though out the years I've gained much experience with ASP.NET, Silverlight, Windows Phone and WinRT.

This vote is based on my past comments; it looks like I did not actually vote at that time.

Your idea of using enumeration types as a discriminator for dispatch of a call to delegate as a replacement of switch/case construct is quite good. Actually you can use any type (interesting example: typeof(Type)).

1) More typically, you should use anonymous methods for delegates (arguments passed to add).2) You don't need to make delegate type a generic type argument; instead, you could make the delegate type itself generic (with type argument 'enum') and add one 'enum' argument passed in the delegate call (or you can combine both approaches); the 'enum' value is useful information for implementation of the delegate.You can even use the same delegate implementation for some subset of key values if you need similar but slightly different processing for different keys in the same delegate implementation. One ridiculus way of using this would be creating a shorter swith operator inside a delegate implementation! (No, I don't advise doing this in practice...)

I confirm by my experience that this approach is quite correct.Event though you slightly add to memory footprint, you win performance, not only convenience and supportability.My 5.I would advise one improvement: add one more method accepting the delegate argument: something like SetDefaultAction to cover all values not found in the dictionary. Initially, this default action should be null.

You've come up with a very elegant solution! I am curious though; what kind of speed difference is there between this solution and the switch blocks? Obviously its very minimal BUT do this a million times and the difference may be more than trivial. Do you have any idea on this?

If you look at the comment of Jamie Nordmeyer. He has taken a look at the amount of IL produced in both cases. This is about the same. I havn't been able to do some speed tests. I will, but I do not have the time at the moment.

Timmy, the amount if IL code has little to do with actual performance.

My prediction is that your solution will usually give better performance if you compare your implementation with switch-based implementation with the the number of keys (create by your sequence of add methods) being the same as the number of switch cases; more exactly, your solution will be faster if this number is big enough.

Design of dictionaries is based on hash values and -- as I understand it -- backets. This design provides speed much better than linear: close to O(1), according to Microsoft documentation. As switch can only provide linear (O(n)) speed, so your gain in speed compared to switch will grow as linear function with number of cases to be processed; your speed will be close to some constant time span regardless of the number of cases.

Really you provided a good solution to reduce complexity, but I think one of the good benifits of it is the idea of using objects in the switch statement instead of integers and enums, this way we can use any objects we want.

Sincerely Samer Abu Rabie

Note: Please remember to rate this post to help others whom reading it.

I was writing a program to match code in abstract syntax trees. Basically parsed C# code. This leads to a bunch of small methods that specifically match one statement/expression to another. For example two assignment statements. I made a static class that had a method for each statement/expression type and used reflection to build the dictionary that cross referenced the statement/expression type to the method that dealt with it. My methods looked like: public static void MatchAssignmentStatement(AssignmentStatement left, AssignmentStatement right) {...}I then used Linq.Expression to build and compile a delegate appropriate for each. This gets you away from having to hard code building the dictionary.

Another different but similar thing I've done is use the text templating engine built into Visual Studio 2008. These are files with the .tt extention. I would use reflection in the template itself to build a C# class. In your case, you could use the enum to build the code to populate your dictionary.

The following code is from my project on CodePlex at http://www.codeplex.com/JSLRefactorExplorer. It's in the MatchMethodLookup.cs file. I will probably be cleaning and refactoring it sometime in the future but right now I've got other things to do. All in all the codes pretty simple if you understand reflection, Linq and Linq expressions. The project is in its very early stages. I'm planning on making big changes which will change this code slightly.

The objective of the code is to build a dictionary with a Type as the key and a delegate as the value. The Type that I'm passing in has a bunch of static methods starting who's names start with "Match" and take three parameters. The important parameters are the second and third ones. They have to be the same and derive off of CodeUnit or CsElement. All that is the WHERE part of the Linq query in the BuildLookupTable(Type type) method.

The return statement takes the Linq query and builds a dictionary out of it. That's about it for that method.

The CreateDelegate method takes the MethodInfo found in the above method and creates a delegate that's defined in another file in the project:

The method builds the three parameters for the delegate, adds a single statement which is a method call and compiles the expression. The second and third parameter types of the methods being called implement ICodeUnit. For example AssignementStatement is an Abstract Syntax Tree element that implements ICodeUnit. Since the methods being called by my Linq expression are "more specific", the Linq expression has to take in two ICodeUnit's and convert them to the appropriate concrete types. Your example was working with enums so you wouldn't have to do this step.

So to wrap things up... The performance hit from the reflection and the Linq compiling is very small. Best of all, I only have to add a new "Match" method to the appropriate class and it automatically gets use! There's no need to add code to add a new item to a dictionary, etc.

Here's the code from my CodePlex project. It will probably change in the next couple days but the process will be similar.

I've actually done this in a couple of production apps, and it worked great! Much easier to read. I'd be interested in running a test case at some point to see how this method compares to a switch statement in the generated IL, though.

That's what I thought too. Are you using unit tests too? If you test this constuction I'd be very much interested in the results. I do not have time to run a test case in the next few weeks, maybe after that.

So, it looks like the switch statement produces about the same amount of IL. The dictionary method is still nice, as it allows for dynamic control of the "switching" at run time. I used the following code:

My pattern for this has been to use the switch statement when I'm only comparing a few things, but to use a dictionary when I need dynamic control of the structure, or have a lot of conditionals (as a switch statement with 20 cases is incredibly ugly).

I like the way you did this. The first thing that came to mind was to create a dynamic "switch" using reflection to pull in methods at design time. I havn't put many brain cells towards (and I haven't had enough coffee to do that yet), but I think might be worth checking out.

I like the elegance of your solution. However there is also another approach to your described problem. It is known as the "Chain of command" design pattern.http://www.cs.clemson.edu/~malloy/courses/patterns/chain.html

The beauty of this design pattern lies in the fact that every class in the chain can decide if it is capable of handling the call or pass it on to it's successor.