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
- Drew Colthorp’s Explorations in Adapting Redux to C# post
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}
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!