π― 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.”
