A Less Ugly Switch Statement For C#

I know switch statements are considered “evil” because they are very procedural code, they violate the Open-Closed Principle, etc etc. But every now and then I don’t see a need to do anything more than a switch statement because the complexity of the scenario does not need anything more, and the code’s maintainability is not going to be so adversely affected by it.

I had such a case today, when examining the “Status” value – an enum – that is found on the data structure returned by a web service call. Here’s the original code:

   1: switch (newContainer.Status)

   2: {

   3:     case ScannedAssetState.ConfirmAssetRemove:

   4:         {

   5:             ConfirmAssetRemove(newContainer.TagInfo, assetTag);

   6:             break;

   7:         }

   8:     case ScannedAssetState.Error:

   9:         {

  10:             View.ShowError(newContainer.Errors.FirstOrDefault());

  11:             break;

  12:         }

  13:     case ScannedAssetState.Success:

  14:         {

  15:             RestartWithNewContainer(newContainer.TagInfo);

  16:             break;

  17:         }

  18:     default:

  19:         {

  20:             Init(newContainer.TagInfo);

  21:             break;

  22:         }

  23: }

Though this code isn’t horrible, I do tend to hate the excessive syntax of switch statements. So… I wrote a simple fluent API to condense this code. Here’s the end result:

   1: Switch.On(newContainer.Status)

   2:     .Case(ScannedAssetState.ConfirmAssetRemove, () => ConfirmAssetRemove(newContainer.TagInfo, assetTag))

   3:     .Case(ScannedAssetState.Error, () => View.ShowError(newContainer.Errors.FirstOrDefault()))

   4:     .Case(ScannedAssetState.Success, () => RestartWithNewContainer(newContainer.TagInfo))

   5:     .Default(() => Init(newContainer.TagInfo));

And here’s the simple way I implemented it:

   1: public static class Switch

   2: {

   3:     public static Switch<T> On<T>(T value)

   4:     {

   5:         return new Switch<T>(value);

   6:     }

   7: }

   8:  

   9: public class Switch<T>

  10: {

  11:     private bool hasBeenHandled;

  12:     private readonly T value;

  13:  

  14:     public Switch(T value)

  15:     {

  16:         this.value = value;

  17:     }

  18:  

  19:     public Switch<T> Case(T comparisonValue, Action action)

  20:     {

  21:         if (AreEqual(value, comparisonValue))

  22:         {

  23:             hasBeenHandled = true;

  24:             action();

  25:         }

  26:         return this;

  27:     }

  28:  

  29:     public void Default(Action action)

  30:     {

  31:         if (!hasBeenHandled)

  32:             action();

  33:     }

  34:  

  35:     private bool AreEqual(T actualValue, T comparisonValue)

  36:     {

  37:         return Equals(actualValue, comparisonValue);

  38:     }

  39: }

I’m sure the AreEqual method could use some work to make it more robust, but it satisfied my current need just fine.

Of course, this code is nothing ground-breaking and I’m sure plenty of people will tell me how much worse it is than the original, and how I should have / could have avoided a switch statement in the first place… Meh… It was a fun little exercise. We’ll see how long it actually lives in my code base.


Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Derick Bailey

Derick Bailey is an entrepreneur, problem solver (and creator? :P ), software developer, screecaster, writer, blogger, speaker and technology leader in central Texas (north of Austin). He runs SignalLeaf.com - the amazingly awesome podcast audio hosting service that everyone should be using, and WatchMeCode.net where he throws down the JavaScript gauntlets to get you up to speed. He has been a professional software developer since the late 90's, and has been writing code since the late 80's. Find me on twitter: @derickbailey, @mutedsolutions, @backbonejsclass Find me on the web: SignalLeaf, WatchMeCode, Kendo UI blog, MarionetteJS, My Github profile, On Google+.
This entry was posted in .NET, C#, Refactoring. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://www.lucisferre.net Chris Nicola

    I like it, though I’m not sure I could sell it to some .NET code purists. Some people are really opposed to any use of lambdas. I suppose beauty is in the eyes of the beholder.

  • http://turbulentintellect.blogspot.com Chris Ammermant

    Extra credit: What if you have a situation where you want the drop-through (i.e. you would not use break)?

  • http://blog.einbu.no Arjan Einbu

    Its always nice to see new uses of extensions and lambdas, but I don’t believe you’ve increased the readability of the code with this one…

    So what did you gain by this trickery?

    Btw: Removing the unnescessary braces in your original code would be a better way to compact your code in terms of LOC and make the typing about the same…

  • http://www.lostechies.com/members/derick.bailey/default.aspx derick.bailey

    @Chris – i’m sure you could extend the syntax to support that. there’s probably a point of diminishing returns on that, though.

    @all

    forgot to mention one advantage here… you can use this Switch.On on anything you want, not just “integral” values like the standard switch wants.

    so you can do a Switch.On(new Object()) if you wanted… any valid value works – objects, primitives, nulls… anything you want.

  • http://www.lostechies.com/members/derick.bailey/default.aspx derick.bailey

    @Arjan – yeah, didn’t really gain much, other than being able to use any value you want, instead of just integral values. i mostly just wanted to see what it would be like…

  • http://blog.mikeobrien.net hcoverlambda

    I think your functional/declarative version looks a lot cleaner and opens up some additional functionality. Kinda reminds me of the match statement in F#.

    Only thing I would be wary about is that primitive C# constructs might be able to take advantage of optimization by the C# compiler in some way, whereas the functional version would not. I have no idea if that would be true of the case statement.

  • http://www.squaredroot.com Troy Goode

    Certainly being able to use a switch on something beyond ints and strings is nice, but I feel like you’re starting to get into “everything is a nail” territory.

    Thought experiment: would you consider using a similar implementation to replace if statements? Ala…

    If.IsTrue(()=> blah)
    .Then(() => doSomething())
    .ElseIf.IsTrue(() => blah2)
    .Then(() => doSomethingElse())
    .Else(() => fallback());

  • Tom de Koning

    I prefer the use of static classes in this case over an Enum. You end up with syntax like

    Switch.On(container.State)
    .Case(() => Debug.WriteLine(“ConfirmRemove”))
    .Case(() => Debug.WriteLine(“success”))

    using
    public Switch Case(Action action) where TSpecificType : TSpecification
    {
    if (typeof(TSpecificType) == value.GetType())
    {
    hasbeenhandled = true;
    action();
    }
    return this;
    }

    .Default(()=> Debug.WriteLine(“std”));

  • Tom de Koning

    Then again, I like this syntax best:
    container.State.Case(()=>Debug.WriteLine(“ConfirmRemove”))
    .Case(()=> Debug.WriteLine(“Succcess”));

    public abstract class StaticEnum
    {}
    public abstract class AssetState: StaticEnum
    {
    public static AssetState Remove = new Remove();
    public static AssetState Error = new Error();
    public static AssetState Success = new Success();
    }

    public static class StaticEnumExtensions
    {
    private static StaticEnum value;
    private static bool hasbeenhandled;

    public static StaticEnum Case(this StaticEnum spec, Action action) where TSpecificType : StaticEnum
    {
    value = spec;
    if (typeof(TSpecificType) == value.GetType())
    {
    hasbeenhandled = true;
    action();
    }
    return spec;
    }

    public static void Default(Action action)
    {
    if (!hasbeenhandled)
    {
    action();
    }
    }
    }

    Haven’t fullly tested it yet as I really need to get some sleep. What do you think?

  • http://www.tavaresstudios.com Chris Tavares

    The major reason for the restriction to integral types on switch statements is specifically optimization – the compiler and the jitter detect a switch statement and can optimize the heck out of it. Your construct is probably a LOT slower, what with method calls for every test and every condition.

    But then again, of course, don’t optimize ’til you measure.

  • Kenny Eliasson

    When im dealing with a if or case-statement I usually end up with a Dictionary>.. Though it doesnt have a default-statement..

    Dictionary return Factory.Create(input));
    Dictionary
    return Factory.Create(input));

    And too use it

    public void Accept(MyEnum value, data) {
    return Dictionary[value](data);
    }

  • Sean Stapleton

    I’m not sure what benefits this provides over something simpler (to my eyes), like Dictionary scannedAssetStateAction.

    It seems like the dictionary is an easy place to verify you have handled all states (and could be easily extended/encapsulated to handle default). Plus then the calling code is a short and sweet 1 liner:

    scannedAssetStateAction[newContainer.Status]();

  • http://nomad3k.livejournal.com Chris Kemp

    What you’ve written doesn’t vary much from this, which is just removing the braces and putting it all onto the same line.

    switch (newContainer.Status)
    {
    case ScannedAssetState.ConfirmAssetRemove: ConfirmAssetRemove(newContainer.TagInfo, assetTag); break;
    case ScannedAssetState.Error: View.ShowError(newContainer.Errors.FirstOrDefault()); break;
    case ScannedAssetState.Success: RestartWithNewContainer(newContainer.TagInfo); break;
    default: Init(newContainer.TagInfo); break;
    }

    All I’d say you’ve really achieved is adding an extra bit of complexity that needs extra testing.

  • http://agilior.pt/blogs/rodrigo.guerreiro Rodrigo Guerreiro

    Hi Derick,

    I think that in the Case method, before you check if the values are equal, you should check if it was already handled. If not, then you aren’t simulating the break statement that you have in the original code.

  • DaRage

    Very nice. I definitely like that.

  • Diego F.

    Consider renaming this post: A Uglier, Unnecessary And Slower Switch Statement For C#.

    This title would suit better.

  • http://www.lostechies.com/members/derick.bailey/default.aspx derick.bailey

    @Diego – LOL!

    so tell me – what user is going to notice that this is slower than a regular switch statement? honestly… really… what user is going to notice the extra 2 or 3 milliseconds?

    other than that, yea… i’m teetering on the edge of ridiculous :)

  • DaRage

    @Diego

    I don’t think this is uglier because first it uses generics while the switch statement is limited to numbers, strings. Second there is no curly bracket soup (and that’s what i like most) so it reads better. Last switch semantics are very often useful so and easy to read and understand so it’s good to extend it to other types.

    I would call this the Object-Oriented switch while the old one is the structured switch which i consider a very stinky code smell.

  • http://opletayev.com Mikhail Opletayev

    I don’t know about less ugly but surely a LOT slower. You allocate several objects and use a bunch of indirect jumps over a compiler-optimized jump table. I would argue it’s a pretty poor practice.

    Also, there is no need to use { } brackets in cases:

    switch( myObj.status ) {
    case StatusEnum.Case1:
    doSomething();
    break;
    case StatusEnum.Case2:
    doSomethingElse();
    break;
    default:
    doElse();
    break;
    }

  • Michael Chandler

    I don’t like it. I don’t see it offering anything extra (such as extensibility), just extra complexity over the top of a well known concept.

    I’d prefer to see language/framework support for the Chain-of-responsibility pattern (http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern).

    For example, maybe something in the BCL like:

    interface IHandler
    {
    bool IsHandlerFor(TContext context);
    void Apply(TContext context);
    }

    [this is basically identical to ICommand, perhaps we could just reuse that...?]

    You can then either explicitly create a class for each handler, or create a ‘reusable’ class, something like:

    class Handler : IHandler {
    private Func
    _isHandlerFor;
    private Action
    _applier;
    public Handler(Func
    isHandlerFor, Action applier) {
    _isHandlerFor = isHandleFor; _applier = applier;
    }
    bool IsHandlerFor(TContext context) { return _isHandlerFor(context); }
    void Apply(TContext context) { _applier(context); }
    }

    You could then do something like:
    var handlers = new[] {
    new Handler(context => context.Status == ScannedAssetState.ConfirmAssetRemove, context => ConfirmAssetRemove(…)),
    new Handler
    (context => context.Status == ScannedAssetState.Error, context => View.ShowError(…)),
    }

    ApplyFirstMatchingHandler(newContainer, handlers);

    This also gives you the flexibility of allowing the IoC container to automatically setup the handlers for you, if you so wish. If you did do that, then as new handlers are created (say for a new state), none of the existing code needs to change. It also means that the logic for handling that particular state is located only within the relevant handler, leading to higher cohesion.

    Thoughts?

  • Simon Woods

    Don’t know if you’ve had a look at the series starting with

    http://lorgonblog.wordpress.com/2007/12/02/c-3-0-lambda-and-the-first-post-of-a-series-about-monadic-parser-combinators/

    I think you’ll find quite a few control structures implemented in this sort of way (except as extension methods). I think Bart de Smet did something similar with If and For.

    http://bartdesmet.net/blogs/bart/archive/2009/07/11/bart-s-control-library-not-what-you-think-it-is-part-0.aspx

  • http://Nomad3k.livejournal.com Chris Kemp

    the important point I mused from my earlier post is that your solution still violates the open closed principe because to support a new scanned asset state you have to amend your new switch statement, so unfortunately you’ve failed to the core goal of better adherence to solid principles. still, thanks for making me ask Why switch violates open closed, and thinking about it properly. Good post.the important point I mused from my earlier post is that your solution still violates the open closed principe because to support a new scanned asset state you have to amend your new switch statement, so unfortunately you’ve failed to the core goal of better adherence to solid principles. still, thanks for making me ask Why switch violates open closed, and thinking about it properly. Good post.the important point I mused from my earlier post is that your solution still violates the open closed principe because to support a new scanned asset state you have to amend your new switch statement, so unfortunately you’ve failed to the core goal of better adherence to solid principles. still, thanks for making me ask Why switch violates open closed, and thinking about it properly. Good post.

  • http://Nomad3k.livejournal.com Chris Kemp

    the important point I missed from my earlier post is that your solution still violates the open closed principe because to support a new scanned asset state you have to amend your new switch statement, so unfortunately you’ve failed to the core goal of better adherence to solid principles. still, thanks for making me ask Why switch violates open closed, and thinking about it properly. Good post.

  • http://www.cpmcgrath.com Chris McGrath

    Ok, It has it’s place (for things that aren’t switchable) but you loose so much power (when you want to run the same code for 2 cases), it is slower and if you did the switch statement correctly, then your solution is uglier.
    As someone already pointed out, if you remove the braces, put it all on 1 line you have a lighter weight version of what you created.

  • http://stillpearling.blogspot.com David Clarke

    Nice post about one of my pet peeves with C#. When the opportunity presented to fix the C switch statement, what happened instead? It got more broke! C# switch has no fall through which I don’t object to; the small number of scenarios where fall through might be useful aren’t a good reason to keep semantics that are responsible for a vast number of defects. So given there is no fall through, why the hell do we need to use a “break” to exit the current condition. Surely it would have been better to use the standard block semantics which you hint at in your post by wrapping braces round the conditional statements. Without braces – execute single statement and exit switch, with braces – execute block and exit. Crying. Spilt. Milk.

    When you suggest the performance of your fluent code is an unnoticeable amount slower than a switch statement, you may be correct where the code is executed infrequently. But if the switch is inside a loop that slight difference can turn into a substantial delay. As always, use with caution.

  • http://thinkbeforecoding.com thinkbeforecoding

    One of the things you can do with such construct is to create a functional switch, ie a switch whose result is a value instead of a switch between statement.
    The if statement as the ?: expression, but the switch statement hasn’t.

    It’s very usefull when building trees with Linq to Xml…

  • :o)

    Wow. You’ve removed a few curly braces but introduced unnessecary complexity in the form of lambdas and degraded performance. The most worry thing though is you’ve obfuscated a basic control flow operation understood by everyone which will lead to head scratching for any developer that takes on the code after you.

    Worth it just to get rid of a few braces and breaks?

  • Gever Lances

    see here some more conditional statements C# conditions
    Gever