Page 26 - MSDN Magazine, May 2019
P. 26

Figure 3 Positional Pattern
I could also say that I require the point to be uninitialized, and only handle those uninitialized scenarios. This is a lot less verbose than using the positional patterns, and it works very well for situations where you can’t add a deconstruct method to the type you’re matching.
Finally, I have the tuple pattern, which leverages the positional pat- tern and allows me to compose a tuple on which to run my match. I can illustrate this with a scenario where I operate on different states, such as opening, closing and locking a door (see Figure 4). A particu- lar situation may occur depending on the current state of the door, the operation I want to perform and the key I may possess. This example of using the Tuple pattern to introduce a state machine is one commonly used by C# Design Lead Mads Torgersen. Check out Torgersen’s post, “DoMorewithPatternsinC#8.0,”atbit.ly/2O2SDqo.
The code in Figure 4 first constructs a new tuple containing the current state, the desired operation, and a Boolean checking if the user has a valid key or not. It’s a very simple scenario.
While this is a welcome addition to the language, please resist the urge to use switch expressions for every if/else if/else condition.
Based on these different values, I can match on different situa- tions by constructing more tuples, together with a positional pattern. This is the tuple pattern. If I try to open a door that’s closed, but not locked, that will result in a new state telling me that the door is now open. If the door is locked and I try to unlock it with an invalid key, the door will remain locked. If I try to open a door that’s opened, I get an exception. You get the idea. This is a very flexible and inter- esting way to approach a situation that previously was very verbose and produced code that was a lot less readable.
Final Words
The pattern-matching improvements in C# 8.0, together with the switch expression, will definitely change the way developers write applications. C# is nearly two decades old and has evolved to reflect the way that applications are built. Pattern matching is sim- ply the latest expression of that evolution.
For developers, it’s wise to be cautious about overusing these new principles and patterns. Be mindful about the code that you write, and make sure that it’s readable, understandable and main- tainable. That’s all that your fellow developers ask for, and I reckon that these changes to the language will help improve the signal-to- noise ratio of the code you produce. n
Filip EkbErg is a public speaker, Pluralsight author, principal consultant and author of “C# Smorgasbord” (2012). Ekberg has worked all the way from Sydney to Gothenburg, and has more than a decade of experience with C#. You can contact him through Twitter: @fekberg or via filip@ekberg.dev.
Thanks to the following Microsoft technical expert for reviewing this article: Bill Wagner
Shape shape = new Rectangle {
Width = 100, Height = 100,
Point = new Point { X = 0, Y = 100 } };
var result = shape switch {
Rectangle (100, 100, null) => "Found 100x100 rectangle without a point", Rectangle (100, 100, _) => "Found 100x100 rectangle",
_ => "Different, or null shape"
};
givenvaluesthatyougetoutofthedeconstruction.Giventhefact that you have a way defined to deconstruct the rectangle, you can express a pattern that leverages the position of the output like what you see in Figure 3.
First, let’s match the type of shape. In this case I only want to match it against a rectangle. The second applied pattern, when matched to a rectangle, uses the deconstruct method together with the tuple syntax to express which values I require for each particular position.
I can specify that I explicitly want the point to be null, or I can use the underscore to express that I simply don’t care. Keep in mind that the order matters very much here. If we’d have the version where we don’t care on top, it would always match on that pattern even if the rectangle has a point or not. This is known as the positional pattern.
This is very handy if I have a deconstruct available, although if the deconstruct outputs a lot of values, it gets rather verbose. This is where the property pattern comes into play. So far I’ve matched on different types, but some scenarios require you to match on other things, such as state or just looking at the different property values, or the lack thereof.
As the following code describes, I don’t care which type I get, as long as it matches a type containing a point, where this point has a value of 100 in the Y property, like so:
shape switch {
{ Point: { Y : 100 } } => "Y is 100",
{ Point: null } => "Point not initialized", };
Observe that the code doesn’t in fact handle cases where the shape is null, or when the point is initialized but has a different Y value than 100. In those situations, this code will throw an excep- tion. This could be solved by introducing the default case using the underscore.
Figure 4 Tuple pattern
var newState = (state, operation, key.IsValid) switch {
};
(State.Opened, Operation.Close, _) (State.Opened, Operation.Open, _) "Can't open an opened door"),
(State.Opened, Operation.Lock, true) (State.Locked, Operation.Open, true) (State.Closed, Operation.Open, false) (State.Closed, Operation.Lock, true) (State.Closed, Operation.Close, _)
"Can't close a closed door"), _ => state
=> State.Closed,
=> throw new Exception(
=> State.Locked,
=> State.Opened,
=> State.Locked,
=> State.Locked,
=> throw new Exception(
20 msdn magazine
C#


































































































   24   25   26   27   28