Replace Nested Conditionals with C# Switch Expressions and Property Patterns
Nested if-else chains hide intent. C# switch expressions with property and relational patterns turn multi-field domain rules into declarative arms the compiler can check.
15 Sept 2025, 00:29 UTC

Nested if-else chains for domain rules become hard to read the moment you need to check two or three properties together. The useful takeaway is that C# switch expressions with property patterns let you express those multi-property checks declaratively, and the compiler helps you keep the mapping complete.
The problem with nested conditionals for domain rules
A common shape is an object with several relevant properties, for example an Order with Status, Amount and CustomerTier. The old approach is a cascade of if-else with manual null checks and range tests.
That code works, but the intent is buried in boolean logic. Adding a new status or a new tier means editing deep branches, and it is easy to miss a combination. Switch expressions, introduced in C# 8 and extended through C# 12, map a value to a result through patterns in a single expression.
What switch expressions and property patterns buy you
A switch expression assigns a result directly from a pattern match instead of executing statements. Property patterns let you test object properties inside the pattern itself, like order is { Status: "Paid", Amount: > 1000 }.
Relational patterns use <, >, >= and <= inside a switch arm to replace manual range checks. The discard pattern _ is the catch-all arm that makes the expression exhaustive. If you omit it, the compiler rejects the switch as incomplete, and for enums it names the missing members.
Order matters. More specific patterns must appear before general ones, otherwise a broader pattern shadows a later, more specific arm. The compiler will not reorder them for you.
Worked example: pricing tier from an order
Assume a simple domain model:
record CustomerTier(string Name);
record Order(string? Status, decimal Amount, CustomerTier? Tier);A switch expression can classify the order without nested ifs:
string Classify(Order order) => order switch
{
null => "Invalid",
{ Status: null } => "Missing status",
{ Status: "Paid", Amount: >= 1000, Tier: { Name: "Gold" } } => "Priority Gold",
{ Status: "Paid", Amount: >= 1000 } => "Priority",
{ Status: "Paid", Amount: >= 100 } => "Standard Paid",
{ Status: "Pending", Amount: > 0 } => "Awaiting payment",
{ Status: "Cancelled" } => "Cancelled",
_ => "Unknown"
};The first arm uses the null pattern for a null reference. Property patterns test Status, Amount and the nested Tier.Name in the same arm; a null Tier fails the nested pattern and falls through to the next arm. Relational patterns handle the amount ranges, and the final discard arm is the fallback.
To try this locally, run dotnet new console in a project targeting .NET 6 or later (C# 10), paste the records and the function, and call Classify with different Order instances. Enable nullable reference types with <Nullable>enable</Nullable> in the project file so the compiler warns when a null Order can reach the method.
Trade-offs and when to stop
Pattern matching is excellent for classification and mapping, but dense nested patterns can be hard to debug. If a single arm contains three levels of property deconstruction, extract a helper method whose name explains the rule.
Performance is negligible for typical application code. Extremely deep recursive patterns can increase stack depth, so keep patterns flat on hot paths.
Pattern order is a common source of bugs. Review arms from most specific to least specific, and when you switch on an enum, remove the discard arm temporarily so the compiler lists any cases you forgot.
Check it in your codebase
Find a method with three or more nested ifs that inspect the same object. Rewrite it as a switch expression using property patterns for the fields you care about. Build and confirm it compiles cleanly, then add unit tests for null, boundary amounts, and each status combination to confirm the mapping.
Use switch expressions for decision tables and property patterns for multi-field checks. Keep arms readable, order them from specific to general, and extract a method when complexity grows.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.