Do you ever feel like (well, you should) these huge switch + case statements or too many ifs seem to be wrong? What if I told you, there’s one simple trick that will change your life, by getting rid of them? Ok, seriously – I have nothing against switch or if as the way of controlling the flow (I use them quite often) however, there are certain occasions at which the things could be done better. And let me show you another way to achieve the same goal which is much cleaner in terms of code readability and maintenance.
This post is all about coding, so let’s include some code in here. Here comes our “problem”:
1 2 3 4 5 6 7 8 9 10 11 12 |
enum Operation { NotSet = 0, Cancel = 1, Block = 2, Suspend = 3, Disable = 4, Remove = 5, Approve = 6, Activate = 7, Create = 8 } |
This guy above surely may possess many more types, it could be also a magic string or anything else that makes sense (sorry, magic strings actually make no sense).
Anyway, you get the idea – there’s a variable, and based on its value that has to be checked first, we need to perform some action. BTW, before you start judging me – I’m aware that I don’t need to assign numerical values to the enums, but I like to be explicit about what I’m doing with my code, so I tend to follow this practice.
Back to the point, there’s a function that returns the type of operation that has to be dealt with:
1 2 3 4 5 6 7 |
public void ExecuteOperation() { var operation = ResolveOperation(); //Now do something with this operation, switch/if anyone? } private Operation ResolveOperation() => //return the operation based on the current state of the application. |
First solution – switch + case:
1 2 3 4 5 6 7 8 9 10 |
switch (operation) { case Operation.NotSet: DoSomething(); break; case Operation.Cancel: DoSomethingElse(); break; //And many more lines below. } |
Could be simplified a little bit if we could just use return statement and get rid of break keyword, yet still, it doesn’t make much difference for many available options.
Second solution – what if:
1 2 3 4 5 6 7 8 9 10 |
if (operation == Operation.NotSet) { DoSomething(); return; } if (operation == Operation.Cancel) { DoSomethingElse(); return; } |
Same problem for too many options, could be simplified as well if the return value was used, but in the end, it doesn’t matter that much.
Third and final solution – dictionary of mappings between operation and function:
1 2 3 4 5 6 |
static readonly IDictionary<Operation, Action> Operations = new Dictionary<Operation, Action> { [Operation.NotSet] = () => DoSomething(), [Operation.Cancel] = () => DoSomethingElse(), //Moar mappings. }; |
What did I do? Well, I’ve just created very simple mappings between the available operations and functions. These mappings can be much more sophisticated ones e.g. you may use Func in order to provide some input parameters or return a value.
And what happens now, if we want to perform an operation? This single line of code:
1 |
Operations[operation](); |
Quite easy, isn’t it? And you can even split your mappings declarations within dictionary into separate functions if there’s too many of them. The general idea, though, is that instead of using old fashioned switch/if statements for controlling the flow (that might grow tremendously) you just say: “Hey, let me point this operation to this function, and just invoke it if there’s a match” and create a dictionary or any other suitable collection to achieve the goal.
I do realize that the presented solution ain’t nothing fresh or new and it’s been there for years, yet there’s still so much code written using this old C style, so I hope that at least some of you will find this to be useful.
Pingback: Get rid of switch/case/if - How to Code .NET
Using enum for key will lead to boxing on every access.
Good point, the question is does it really make a difference in terms of performance here? 🙂
Still, the point is that you can use as a key any type that you prefer, yet enum happens to be one of the most common examples.
Yea the main benefit for me in this approach is that you separate two things – calling and selecting functions which make code much more testable
Good point about testing.
I would go even further and inject implementation, provided its immutable as most IoC containers offer factories for such mappings, such as IIndex in Autofac. 🙂
Nice idea about abstracting all that stuff away into the IoC container :).
I don’t like “Operations[operation]();” syntax – I know it is valid etc.. but just looks… strange 🙂 Just like compiling lambda expression and invoking returned delegate: “myLambdaExpr.Compile()()”. .
It does, I would assign the Action to a variable and then invoke it :). Anyway, I want the examples to be as short as possible.
Congratulations! You’ve invented method dispatching. A little effort more, and you’ll invent object oriented programming, by storing those method dispatching tables in structures, and by passing a pointer to those structures to each of those functions…
Thanks, I have really tried hard to do my best 😀 ;).
if you have more than one methpd like that, you can create hierarchy of classes and have factory method that will based on enum create Object of particular class. This way you will only have one instance of switch-case in your code, and all methods in your class heirarchy going to be virtual and easily accessible via base class typed variable.
There are generally two approaches. Functional one tend to have dummy data structures with no methods defined on them and set of methods for your data structure each of the generally having pattern matching construct at top level.
Object oriented approach is to have factory, that will be your only switch statement.
Good idea with the factory, ultimately that’s what one should try to achieve – abstract away as much repeatable code as possible. Thanks for the well explained solution.
The beauty here lies in it’s functional & declarative programming roots, i.e. treating code as data.
Extracting the association between an operation and it’s action makes it easier to represent this data in any form. You could have dictionaries, read a json file, use attributes, etc.
Actually, I didn’t think about it in terms of storing such a structure within some configuration file, seems like a viable option.
Can do it like this too: http://paweltymura.pl/2016/01/13/chain-of-respnsibility-bo-po-co-ci-switch/ (link to Polish article)
That’s a great pattern, thanks for posting!
If you’re concerned about boxing related to the use of an enum keyed dictionary, I wrote a library that handles this issue (and a lot of other enum corner cases) called DiagonacticEnumsExtensions on GitHub provides an IEqualityComprarer that eliminates the problem (it uses the underlying value with an unsafe cast operation — the whole thing is written in C++/CLI because C# doesn’t allow enum constraints — it’s compiled AnyCPU and covered to death in tests). That handles the slowdown if that kind of micro-opimisation is needed.
This post inspired me to add a helper class that allows assigning delegates to enum values. I’ll probably have that added this week. Not sure why I didn’t think of that earlier, I do something similar rather regularly.
Matthew, it’s very nice to hear that I’ve somehow inspired you do extend your library :). Personally, I’m not concerned that much with the boxing issue, but I’m pretty sure that other people will find it useful, good job!
Thanks! I wrote the implementation an hour ago and it’s passing tests. I’m going to sleep on it because I always think of something I’ve missed after a good rest so I’ll probably have it released tomorrow evening.
I should have also been more clear in my previous comment, it’s a NuGet package “DiagonacticEnumExtensions” and the page links to its GitHub page.
Well, that was quick, cool! BTW I think you’ve made a typo in your package name?
You are correct, sir! Thanks for pointing that out. It’s “DiagonacticEnumsExtensions”, available here: https://www.nuget.org/packages/DiagonacticEnumsExtensions/
I’m about to upload the new version in a few hours here.
Nice article, but for me every presented approach have serious flaw. You can use if/switch/dictionary but if you *add* another constant to enumeration and forget to update your if/switch/dictionary you will not get compile time error. OK unit testing can catch errors like this but still we may expect something better from C#…
This problem can be solved be declaring base class that will represent enum and subclasses that will represent enum constants and by moving code invoked for each constant into subclasses methods (there will be also abstract methods in base class). This will give us compile time errors, but of course it is not always applicable (e.g. in factory methods). BTW enums are implemented just like that in Java.
You’re correct, but the whole point of this article was not to get rid of enum or any other type and replace it with a nice class. It’s all about dealing with enum or magic string when you can’t do much about that fact (e.g. replacing enum with a custom class).
If that’s what you are after, you might be interested in my library OneOf, which lets you do something similar to f# discriminated unions.
https://github.com/mcintyre321/OneOf
That’s an interesting pattern, I’ll take a look at this!