Pattern Matching in C# with Switch Statements

Article summary

While trying to figure out the best way to structure my “reducer” functions in a Redux-style Xamarin app, I found out that C# 7.0 introduced pattern matching support in switch statements!

From the New Features in C# 7.0 post:

We’re generalizing the switch statement so that:
* You can switch on any type (not just primitive types)
* Patterns can be used in case clauses
* Case clauses can have additional conditions on them

This means that in addition to being able to compare a value to a constant string or number (which were the only things the previous switch statement supported), you can now switch on the type of an object. And even better, it will automatically cast the object, assigning it to a local variable that can be used within the scope of the case block.

Here’s an example based on one of the reducer examples from the Redux documentation:


private static State TodoApp(State state, IAction incomingAction)
{
		switch (incomingAction)
		{
				case Actions.SetVisibilityFilter action:
						return UpdateFilter(state, action.Filter);

				case Actions.AddTodo action:
						return AddTodo(state, action.Text);

				default:
						return state;
		}
}

In this example, each case block will have its own action variable that’s correctly typed based on the type of the IAction that’s passed in. In this case, the SetVisibilityFilter action has a Filter property and the AddTodo action has a Text property. You’ll get complete type checking and intellisense for the action variable in each case block.

When Clauses

In addition to being able to switch on any type of object, when clauses can also be added to get even more specific:


private static State TodoApp(State state, IAction incomingAction)
{
		switch (incomingAction)
		{
				case Actions.SetVisibilityFilter action:
						return UpdateFilter(state, action.Filter);

				case Actions.AddTodo action when action.Text.StartsWith("!"):
						return AddImportantTodo(state, action.Text);

				case Actions.AddTodo action:
						return AddTodo(state, action.Text);

				default:
						return state;
		}
}

In this example, if the AddTodo action has a Text value that starts with !, it will be handled differently (by the AddImportantTodo handler instead of the AddTodo handler). If not, it will fall through to the normal AddTodo handler.

Conclusion

Pattern matching in switch statements can be extremely useful, especially if you’re structuring your code in a more functional programming style. With this support added to the switch statement in C# 7.0, C# provides greater flexibility in letting developers structure code in the way that most makes sense for each application.

Be sure to check out The C# Guide Page on Pattern Matching to see some other interesting ways you can use pattern matching in C#.

Additional resources

Conversation
  • Oleg says:

    IMO, the given samples reveals antipattern because of lack redability,
    Using the incomingAction as variable is enough.
    {code}
    switch (incomingAction)
    {
    case Actions.SetVisibilityFilter:
    return UpdateFilter(state, incomingAction.Filter);

    case Actions.AddTodo:
    return AddTodo(state, incomingAction.Text);

    default:
    return state;
    }
    {code}

    • Hiral says:

      I agree, the sample does reveal an anti-pattern. Though I would consider abstracting things out a bit more as well as adding incomingAction as a variable. I found this blog post which also describes switching on types in C#, I think this may help Oleg.

      Cheers!

  • Comments are closed.