Refactoring a Switch statement
Switch, select, case, really long if statement…whatever the cause, when they get too long they all become unmanageable. Overly long switch statements do exists, I’ve written them, we have all seen them in other peoples code. In fact, I remember hearing, from a fellow Elegant Coder, that a coworker of his hit the limit for how many cases can be in a .Net switch statement. I don’t remember what the limit was, but it was a lot, and they exceeded that number.
Side Note: what do you do in that situation? Someone else wrote it, and is in a fix. In this case, the esteemed Elegant Coder smiled, said he had no idea, and walked away.
Now, what do you do if a switch statement slowly moves towards a maintenance nightmare? After all, any switch statement, with more than 10 items in it will be larger than any screen output you have — thus moving beyond the ideal of being able to see all the code in a method without scrolling.
My solution: use a Dictionary. Really, a Dictionary. The keys are hashed, retrieval by index is very fast, and we have these cool delegate things and lambda to help us along.
Chris Brandsma’s rules of proper switch statement etiquette:
- A switch statement should really be in its own method, all by itself, away from all other innocent code. I call this switch statement inoculation.
- There should be no more than one line of code in each case, not including the break;
- You should strive to use the return statement instead of relying on breaks. This means that one line of code should return something. If that is not the case…user the break.
- Best case, each case of a switch statement performs an action, and each action is in a separate class.
Following those three rules will better help you refactor switch statement code in the future. But there are still a few reasons for not liking this. First off, switch statements are very static. To add a new item to statement you have to recompile. There are times when you would like the list to be a bit more dynamic. Maybe you would like the list to be loaded via an IoC library.
Now on to the refactoing…
Step 1: Figure out what each case statement needs to run. Typically all the code in a case statement needs a common set of data to execute. Figure out what that is.
Step 2: Create a delegate for passing all of these values to each case as if it were a method.
Step 3: Convert all of the code in each case to a method that follows the delegate just created.
Step 4: Create a Dictionary that implements the delegate, and initialize the dictionary with the methods.
Step 5: Replace the switch with a single call to Invoke on the delegate.
Just show me some darn code
I’ve included a sample project, so you can just download it and check out the code from there. I’ve included samples for .Net 2.0 (C# 2.0) and .Net 3.5 (C# 3.0) in the project. I’m using .Net 3.5 code here.
For step one, here is a class that I created that contains all of the methods my switch/dictionary is going to call:
Also, my original switch statement looked like this:
Now the refactored version:
When to do this
Something I’ll point out right away, there is more code in the refactored version. But removing code is not always what we are after, we are after maintainable code. For the purposes of this demo, I kept things simple, we were not dealing with a large switch statement to begin with.
If you go through the switch statement rules above and you still need something else, then bring this out. But, I do not see this as an automatic way to upgrade some code.
That is the thing with refactoring, you are never really done. You can move the dictionary initialization to its own class. Or you can replace the dictionary with an IoC. Depends on what you need.