Skip to content

Bits of .NET

Daily micro-tips for C#, SQL, performance, and scalable backend engineering.

  • Asp.Net Core
  • C#
  • SQL
  • JavaScript
  • CSS
  • About
  • ErcanOPAK.com
  • No Access
  • Privacy Policy
C#

C#: Use Pattern Matching for Cleaner Type Checks and Switches

- 22.03.26 - ErcanOPAK

🎯 Switch on Steroids

Type casting everywhere? Nested if-else chains? Pattern matching makes type checks and complex conditions elegant.

Type Pattern (is)

// ❌ Old way: Type check + cast
if (obj is string)
{
    string s = (string)obj;
    Console.WriteLine(s.ToUpper());
}

// βœ… Pattern matching: Check and declare in one
if (obj is string s)
{
    Console.WriteLine(s.ToUpper());
}

// Works with null check too
if (obj is not null)
{
    // obj is definitely not null here
}

Switch Expressions

Before (Old Switch)

string GetDiscount(Customer customer)
{
    switch (customer.Type)
    {
        case CustomerType.Regular:
            return "5%";
        case CustomerType.Premium:
            return "10%";
        case CustomerType.VIP:
            return "20%";
        default:
            return "0%";
    }
}

After (Switch Expression)

string GetDiscount(Customer customer) => customer.Type switch
{
    CustomerType.Regular => "5%",
    CustomerType.Premium => "10%",
    CustomerType.VIP => "20%",
    _ => "0%"
};

// Concise, expression-based, returns value directly

Property Patterns

decimal CalculateShipping(Order order) => order switch
{
    // Match on properties
    { Total: > 100 } => 0,                    // Free shipping
    { Country: "US", Total: > 50 } => 5,      // US discount
    { Country: "US" } => 10,                  // US standard
    { IsPriority: true } => 25,               // Priority
    _ => 15                                   // Default
};

// Nested property patterns
string GetRisk(User user) => user switch
{
    { Age: < 18 } => "Minor",
    { Orders: { Count: > 100 }, AccountAge: > 365 } => "Trusted",
    { Orders: { Count: 0 }, AccountAge: < 7 } => "New",
    _ => "Regular"
};

Relational Patterns

string GetTemperatureDescription(int temp) => temp switch
{
    < 0 => "Freezing",
    >= 0 and < 10 => "Cold",
    >= 10 and < 20 => "Cool",
    >= 20 and < 30 => "Warm",
    >= 30 => "Hot"
};

// Or patterns
bool IsWeekend(DayOfWeek day) => day is DayOfWeek.Saturday or DayOfWeek.Sunday;

// Not pattern
bool IsWeekday(DayOfWeek day) => day is not (DayOfWeek.Saturday or DayOfWeek.Sunday);

Type Patterns in Switch

string Describe(object obj) => obj switch
{
    int i => $"Integer: {i}",
    string s => $"String: {s}",
    DateTime dt => $"Date: {dt:yyyy-MM-dd}",
    int[] arr => $"Array with {arr.Length} elements",
    null => "Null",
    _ => "Unknown type"
};

// With property patterns
decimal CalculateArea(Shape shape) => shape switch
{
    Circle { Radius: var r } => Math.PI * r * r,
    Rectangle { Width: var w, Height: var h } => w * h,
    Square { Side: var s } => s * s,
    _ => 0
};

🎨 List Patterns (C# 11)

string Analyze(int[] numbers) => numbers switch
{
    [] => "Empty",
    [var single] => $"Single: {single}",
    [var first, var second] => $"Pair: {first}, {second}",
    [var first, .., var last] => $"Multiple: {first}...{last}",
    [1, 2, 3] => "Exactly 1, 2, 3",
    [var x, > 10, ..] => $"Starts with {x}, second > 10"
};

// Example usage
Analyze(new[] { 5 });           // "Single: 5"
Analyze(new[] { 1, 2 });        // "Pair: 1, 2"
Analyze(new[] { 1, 2, 3, 4 });  // "Multiple: 1...4"

βœ… Benefits

  • Less code: No explicit casts or temp variables
  • More readable: Intent is clear
  • Type safe: Compiler checks exhaustiveness
  • Expression-based: Can use in LINQ, assignments
  • Performance: Same or better than if-else chains

πŸ’‘ Real-World Example

// HTTP status code handling
string GetMessage(int statusCode) => statusCode switch
{
    200 => "OK",
    >= 200 and < 300 => "Success",
    404 => "Not Found",
    >= 400 and < 500 => "Client Error",
    >= 500 => "Server Error",
    _ => "Unknown"
};

// Validation with patterns
bool IsValidEmail(string? email) => email switch
{
    null or "" => false,
    { Length: > 100 } => false,
    var e when e.Contains('@') && e.Contains('.') => true,
    _ => false
};

“Refactored payment processor with pattern matching. 200 lines of if-else became 50 lines of switch expressions. Logic clear at a glance. New payment types? Add one line. Code reviews take 5 minutes now.”

β€” Senior C# Developer

Related posts:

How to get rows count in EntityFramework without loading contents

C# Async Code Hides Blocking Calls

.NET Core JSON Serializer Pitfalls β€” Why Your Properties Don’t Serialize

Post Views: 5

Post navigation

SQL: Read Execution Plans to Find Performance Bottlenecks
C#: Use LINQ Efficiently – Avoid Common Performance Pitfalls

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

April 2026
M T W T F S S
 12345
6789101112
13141516171819
20212223242526
27282930  
« Mar    

Most Viewed Posts

  • Get the User Name and Domain Name from an Email Address in SQL (950)
  • How to add default value for Entity Framework migrations for DateTime and Bool (858)
  • Get the First and Last Word from a String or Sentence in SQL (836)
  • How to select distinct rows in a datatable in C# (805)
  • How to make theater mode the default for Youtube (751)
  • Add Constraint to SQL Table to ensure email contains @ (578)
  • How to enable, disable and check if Service Broker is enabled on a database in SQL Server (564)
  • Average of all values in a column that are not zero in SQL (531)
  • How to use Map Mode for Vertical Scroll Mode in Visual Studio (489)
  • Find numbers with more than two decimal places in SQL (447)

Recent Posts

  • C#: Use Init-Only Setters for Immutable Objects After Construction
  • C#: Use Expression-Bodied Members for Concise Single-Line Methods
  • C#: Enable Nullable Reference Types to Eliminate Null Reference Exceptions
  • C#: Use Record Types for Immutable Data Objects
  • SQL: Use CTEs for Readable Complex Queries
  • SQL: Use Window Functions for Advanced Analytical Queries
  • .NET Core: Use Background Services for Long-Running Tasks
  • .NET Core: Use Minimal APIs for Lightweight HTTP Services
  • Git: Use Cherry-Pick to Apply Specific Commits Across Branches
  • Git: Use Interactive Rebase to Clean Up Commit History Before Merge

Most Viewed Posts

  • Get the User Name and Domain Name from an Email Address in SQL (950)
  • How to add default value for Entity Framework migrations for DateTime and Bool (858)
  • Get the First and Last Word from a String or Sentence in SQL (836)
  • How to select distinct rows in a datatable in C# (805)
  • How to make theater mode the default for Youtube (751)

Recent Posts

  • C#: Use Init-Only Setters for Immutable Objects After Construction
  • C#: Use Expression-Bodied Members for Concise Single-Line Methods
  • C#: Enable Nullable Reference Types to Eliminate Null Reference Exceptions
  • C#: Use Record Types for Immutable Data Objects
  • SQL: Use CTEs for Readable Complex Queries

Social

  • ErcanOPAK.com
  • GoodReads
  • LetterBoxD
  • Linkedin
  • The Blog
  • Twitter
© 2026 Bits of .NET | Built with Xblog Plus free WordPress theme by wpthemespace.com