Mark Needham

Thoughts on Software Development

C#: Using a dictionary instead of if statements

with 22 comments

A problem we had to solve on my current project is how to handle form submission where the user can click on a different button depending whether they want to go to the previous page, save the form or go to the next page.

An imperative approach to this problem might yield code similar to the following:

public class SomeController
{
	public ActionResult TheAction(string whichButton, UserData userData)
	{
		if(whichButton == "Back")
		{
			// do the back action
		}
		else if(whichButton == "Next")
		{
			// do the next action
		}
		else if(whichButton == "Save")
		{
			// do the save action
		}
 
		throw Exception("");
	}
}

A neat design idea which my colleague Dermot Kilroy introduced on our project is the idea of using a dictionary to map to the different actions instead of using if statements.

public class SomeController
{
	private Dictionary<string, Func<UserData,ActionResult>> handleAction = 
		new Dictionary<string, Func<UserData,ActionResult>>
		{ { "Back", SaveAction },
		  { "Next", NextAction },
		  { "Save", SaveAction } };
 
	public ActionResult TheAction(string whichButton, UserData userData)
	{
		if(handleAction.ContainsKey(whichButton))
		{
			return handleAction[whichButton](userData);
		}
 
		throw Exception("");
	}
 
	private ActionResult NextAction(UserData userData)
	{
		// do cool stuff
	}
}

It’s quite similar in a way to a problem we had on another project where we needed to deal with user inputs and then create an object appropriately.

The way we have to read the code is a bit more indirect than with the original approach since you now need to click through to the individual methods for each action.

On the other hand I like the fact that we don’t have if statements all over the place anymore.

* Updated * – updated to take Dhananjay Goyani’s comments into account

Be Sociable, Share!

Written by Mark Needham

May 30th, 2010 at 11:13 pm

Posted in .NET

Tagged with

  • Pingback: Tweets that mention C#: Using a dictionary instead of if statements at Mark Needham -- Topsy.com()

  • Mark, why not use a switch (case) statement?

  • Are you sure it is ActionResult (from MVC) in the second part of dictionary? That will just not work.

    You will want to define your own delegate to store method ref.
    >> delegate ActionResult SomeAction(string someData);

    Alter your dictionary type to account new delegate.
    >> Dictionary dict = //some code//

    Instead of just calling the action method, you may wish to return the result.
    >> return handleAction[whichButton](userData);

    I am sure you must have skipped details and just want to highlight the idea. well, I am just making it consumable. 😉

  • Jake

    I used this technique for a while, as I think it makes the code a lot cleaner. The problem is the knowledge burden it places on the person maintaining your code. This might not be an issue if you’re at a place where you have high quality programmers, but I’ve seen people come across this when going through my code and it always generates a ‘wtf’.

  • @Dhananjay – yeh it is supposed to return the result – I thought I’d actually changed that in the code but obviously not! Will update it – thanks for pointing out.

    @Jake – yeh that’s a better way of describing what I was trying to do with my thoughts about the indirectness we now have in the code.

    @Pieter – because we don’t like switch statements 😛 Perhaps it does make more sense in this case and this is just overcomplication though…

  • Chris

    The delegate dictionary pattern works, but I fail to see any reason you wouldn’t have simple unique event handlers for each button.

    I prefer to save this pattern for little logical machines where you might be processing little sub (or DS) languages. As an example something that could give a result to a math formula like: (4+3)/(3-2)+14. Where you need to write a common handler for multiple actions.

    // something a bit like this
    Calculate(string formula)
    {
    // something a bit recursive
    currentOperation = ReadOperation(formula);
    if (knownOperations.Contains(currentOperation))
    {
    lhs = Calculate(ReadLHS(formula));
    rhs = Calculate(ReadRHS(formula));
    return knownOperations[currentOperations](lhs, rhs);
    }
    throw exception(“”);
    }

    Your example is a bunch of buttons, and I’d much rather just have unique functions assigned to each button (and you end up doing this way).

    Assuming WPF I’d rather have

    Than

    I think you still end up writing the individual handlers in either case. It looks like you’re just going to have more code (and complexity) in your case.

    Can you explain why you didn’t just go with unique handlers?

  • Instead of a two-step ContainsKey/lookup process, I’ll normally do both with a call to TryGetValue:

    public ActionResult TheAction(string whichButton, UserData userData)
    {
    Func func;
    if(handleAction.TryGetValue(whichButton, out func))
    {
    return func(userData);
    }

    throw Exception(“”);
    }

  • Chris

    Boo!! Your blog tool ate my wpf. Using square brackets instead:

    [Button x:Name=”Next” Click=”NextAction” /]
    [Button x:Name=”Back” Click=”BackAction” /]
    [Button x:Name=”Save” Click=”SaveAction” /]

    is better IMO than:

    [Button x:Name=”Next” Click=”TheAction” /]
    [Button x:Name=”Back” Click=”TheAction” /]
    [Button x:Name=”Save” Click=”TheAction” /]

  • David

    I’ve used essentially this approach to simplify a checkout flow and it worked really well. The main benefit for me was centralising that flow logic, which made it easier to see what was happening and to make changes to the flow.

  • I’ve been trying to come up with a name for this pattern.

    http://hackingon.net/post/Unnamed-Refactoring.aspx

  • Pingback: Markus Tamm » Blog Archive » Links 01.06.2010()

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #611()

  • MM

    @Liam – couldn’t this be considered a version of the strategy pattern?

  • Pingback: ginktage.com/links()

  • Stacy

    How about converting a dictionary to a function:

    public static Func ToFunc(this IDictionary dictionary, Func defaultFunc) {
    return key => dictionary.ContainsKey(key) ? dictionary[key] : defaultFunc(key);
    }

    private Func<string, Func> HandleActions()
    {
    return handleAction.ToFunc(buttonName => { throw new Exception(“No action for button: ” + buttonName); });
    }

    public ActionResult TheAction(string whichButton, UserData userData)
    {
    return HandleActions()(whichButton)(userData);
    }

  • @Stacy yeh that seems even better, cool idea.

  • @MM – No, I think this is different.

  • I agree with Chris, is there a particular reason you don’t want to just have separate actions for each button?

    You’re basically creating your own mini routing engine just for this controller, why not just use the one thats already in ASP.NET MVC and route each button directly to the relevant action method? 😉

  • @Chris, @Fredrik – I think we’re doing it this way because we’re not handling the form submission based on the ‘onClick’ but rather just defining a form (with an action) which has several different buttons inside it.

    Is the only way to do the same thing using different actions by having a different ‘onClick’ url for each button?

  • Russ

    A bit prettier…
     
    Func> action;if (handleAction.TryGetValue(whichButton, out action)) action(userData);

  • I agree with Pieter. Switch .. Case is a much simpler and neater way to handle this. Using a Dictionary for this type of task is just complicating things and readability too.

  • El Tinchox

    exactly what I was trying to accomplish, to use strategy with a dicctionary, thanks!