mirror of
https://github.com/jcreek/jcreek.github.io.git
synced 2026-07-12 18:43:50 +00:00
feat(*): Add csharp 8 pattern matching
This commit is contained in:
@@ -0,0 +1,192 @@
|
||||
---
|
||||
layout: post
|
||||
parent: Web Development
|
||||
nav_order: 3
|
||||
title: "C# 8 Pattern Matching"
|
||||
date: 2022-02-15 19:07:21 +0000
|
||||
categories: dotnet csharp
|
||||
---
|
||||
|
||||
# {{page.title}}
|
||||
|
||||
_{{page.date}}_
|
||||
|
||||
This serves as introductory documentation to the pattern matching features introduced in C# 8, which I think are particularly useful.
|
||||
|
||||
## Deconstructors & Positional Patterns
|
||||
|
||||
Adding a deconstructor to your class can be done as below. It must be named `Deconstruct` and be a `public void`. Any values we want deconstructed can be set as `out` parameters, then populated. Here all properties are being deconstructed, but a subset could be deconstructed if desired.
|
||||
|
||||
```csharp
|
||||
class Student
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public int Year { get; set; }
|
||||
public Teacher FormTutor { get; set; }
|
||||
|
||||
public void Deconstruct (out string name, out int year, out Teacher formTutor)
|
||||
{
|
||||
name = Name;
|
||||
year = Year;
|
||||
formTutor = FormTutor;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To use positional parameters, I'll also set up a second model.
|
||||
|
||||
```csharp
|
||||
class Teacher
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Subject { get; set; }
|
||||
|
||||
public void Deconstruct (out string name, out string subject)
|
||||
{
|
||||
name = Name;
|
||||
subject = Subject;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here is an example positional pattern. The discards (`_`) are used to 'match all'.
|
||||
|
||||
```csharp
|
||||
public static class YourClassHere
|
||||
{
|
||||
public static bool IsInYear9English(Student student)
|
||||
{
|
||||
return student is Student(
|
||||
_,
|
||||
9,
|
||||
Teacher (_, "English")
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
While an if statement would probably be easier to read and maintain in this example, positional patterns enable recursion to be used, which can be very useful.
|
||||
|
||||
## Property Patterns
|
||||
|
||||
I've updated the Student model to add a School property.
|
||||
|
||||
```csharp
|
||||
class Student
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public int Year { get; set; }
|
||||
public string School { get; set; }
|
||||
public Teacher FormTutor { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
I prefer using property patterns to positional patterns as they are much more readable. Here's an example method to check whether a student attends a particular school and has a form tutor who teaches Maths.
|
||||
|
||||
```csharp
|
||||
public static class YourClassHere
|
||||
{
|
||||
public static bool IsStudentInGreenAcademyWithMathsFormTutor(Student student)
|
||||
{
|
||||
return student is {
|
||||
School: "Green Academy",
|
||||
FormTutor: {
|
||||
Subject: "Maths"
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This can be made more generic to accept an `object` rather than a `Student`, and check that object is a Student.
|
||||
|
||||
```csharp
|
||||
public static class YourClassHere
|
||||
{
|
||||
public static bool IsStudentInGreenAcademyWithMathsFormTutor(object obj)
|
||||
{
|
||||
return obj is Student student &&
|
||||
student is {
|
||||
School: "Green Academy",
|
||||
FormTutor: {
|
||||
Subject: "Maths"
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Switch Expressions
|
||||
|
||||
These can be used in place of standard switch cases. I'm using a discard (`_`) for catching the default case for unmatched patterns.
|
||||
|
||||
```csharp
|
||||
public static class YourClassHere
|
||||
{
|
||||
public static string DisplayPersonInfo (object person)
|
||||
{
|
||||
string result = person switch
|
||||
{
|
||||
Student student => $"Student at {student.School} in Year {student.Year}",
|
||||
Teacher teacher => $"Teacher of {teacher.Subject}",
|
||||
_ => "Person is not a student or a teacher"
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The syntax can make it easier to read, and it can be a lot more powerful. You can also define recursive switch patterns.
|
||||
|
||||
```csharp
|
||||
public static class YourClassHere
|
||||
{
|
||||
public static string DisplayPersonInfo (object person)
|
||||
{
|
||||
string result = person switch
|
||||
{
|
||||
Student student => student switch
|
||||
{
|
||||
_ when student.Year < 10 and student.Year > 6 => "Student in Key Stage 3",
|
||||
_ when student.Year >= 10 and student.Year <= 13> => "Student in Key Stage 4",
|
||||
_ => $"Student in Year {student.Year}"
|
||||
},
|
||||
Teacher teacher => $"Teacher of {teacher.Subject}",
|
||||
_ => "Person is not a student or a teacher"
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Tuple Patterns (with Switch Expressions)
|
||||
|
||||
You can also use Switch Expressions with Tuples to write even more useful code. For example, you could be creating a game with crafting, combining two items to make another.
|
||||
|
||||
```csharp
|
||||
public static class YourClassHere
|
||||
{
|
||||
public static CraftingMaterial GetCraftingMaterial (CraftingMaterial item1, CraftingMaterial item2)
|
||||
{
|
||||
return (item1, item2) switch
|
||||
{
|
||||
// Match the items in both positions
|
||||
(CraftingMaterial.MountainFlowers, CraftingMaterial.SoulGems) => CraftingMaterial.DwemerMetal,
|
||||
(CraftingMaterial.SoulGems, CraftingMaterial.MountainFlowers) => CraftingMaterial.DwemerMetal,
|
||||
|
||||
(CraftingMaterial.Ore, CraftingMaterial.DwemerMetal) => CraftingMaterial.Ingots,
|
||||
(CraftingMaterial.DwemerMetal, CraftingMaterial.Ore) => CraftingMaterial.Ingots,
|
||||
|
||||
// Handle both items being the same (discard for both, to match any)
|
||||
(_, _) when item1 == item2 => item1,
|
||||
|
||||
// Default case (with discard)
|
||||
_ => CraftingMaterial.Unknown
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This is lovely and easy to read, as well as very powerful.
|
||||
Reference in New Issue
Block a user