This page shows the source for this entry, with WebCore formatting language tags and attributes highlighted.

Title

How pattern-matching in C# is lowered

Description

<img attachment="pattern-matching-in-c-sharp.webp" align="right">A while back, I wrote <a href="{app}/view_article.php?id=4657">Stop trying so hard to use pattern-matching</a>. I stand by everything i wrote there. I was recently mentoring a very clever programmer who's new to C# but has cut his teeth on Rust. We were discussing <c>switch</c> <i>statements</i> vs. <c>switch</c> <i>expressions</i>. Which pattern-matching features can you use where? Which features can you combine? The article <a href="https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/tutorials/pattern-matching" source="Learn Microsoft">Tutorial: Use pattern matching to build type-driven and data-driven algorithms</a> offers a good introduction. Pattern-matching on objects is lovely (and its been available since <a href="https://devblogs.microsoft.com/dotnet/whats-new-in-csharp-7-0/">C# 7.0</a><fn> (2017)). The version they were using still used "switch statements". There's another level called "switch expressions" (available since <a href="https://devblogs.microsoft.com/dotnet/welcome-to-c-9-0/">C# 9</a> (2020)) that they could have used if they were returning a value. The article <a href="https://www.thomasclaudiushuber.com/2021/02/25/c-9-0-pattern-matching-in-switch-expressions/" author="Thomas Claudius Huber" source="">C# 9.0: Pattern Matching in Switch Expressions</a> provides the following example, <code>string favoriteTask = obj switch { Developer dev when dev.YearOfBirth == 1980 => $"{dev.FirstName} listens to metal", Developer dev => $"{dev.FirstName} writes code", Manager _ => "Create meetings", _ => "Do what objects do", };</code> Speaking of syntactic sugar, you can check out what the compiler would generate using the <a href="https://sharplab.io">SharpLab.IO</a>. Throw in any compiling code on the left, and you get the "lowered" version on the right. If you throw in the example from above with a bit of extra code to make it compile, <code>using System; public class C { public void M(object obj) { string favoriteTask = obj switch { Developer { YearOfBirth: >= 1980 and <= 1989 and not 1984 } dev => $"{dev.FirstName} listens to heavy metal while coding", Developer dev => $"{dev.FirstName} writes code", Manager _ => "Create meetings", _ => "Do what objects do", }; } private class Developer { public int YearOfBirth { get; } public string FirstName { get; } = string.Empty; } private class Manager { } }</code> You can see that the generated logic is quite straightforward. The snippet below elides the generated code for the <c>Developer</c> and <c>Manager</c> classes. It's not how I would have written it manually, but I bet it's pretty efficient. <code>[NullableContext(1)] public void M(object obj) { Developer developer = obj as Developer; string text; if (developer == null) { text = ((!(obj is Manager)) ? "Do what objects do" : "Create meetings"); } else { int yearOfBirth = developer.YearOfBirth; if (yearOfBirth >= 1980 && yearOfBirth <= 1989 && yearOfBirth != 1984) { Developer developer2 = developer; text = string.Concat(developer2.FirstName, " listens to heavy metal while coding"); } else { text = string.Concat(developer.FirstName, " writes code"); } } string text2 = text; }</code> <hr> <ft>We're on <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-13">C# 13</a> in April 2025, with <a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-14">C# 14</a> in the works for November, 2025.</ft>