C# Control Flow — if/else, switch, Switch Expressions, and Pattern Matching
In this tutorial, you will learn about C# Control Flow. We cover key concepts, practical examples, and best practices to help you master this topic.
C# control flow statements direct the execution path of your program using conditional branches, with modern additions like switch expressions and pattern matching enabling concise and expressive decision logic.
What You'll Learn
You will master all control flow constructs in C#: the classic if/else statement for boolean branching, switch statements for multi-way branching, switch expressions for concise value-matching, and pattern matching features including property patterns, tuple patterns, and positional patterns introduced in .NET 7 and later.
Why It Matters
Control flow is the backbone of program logic. Choosing the right construct affects code readability, maintainability, and performance. Switch expressions combined with pattern matching reduce boilerplate and make intent explicit. Proper control flow is essential for writing correct business logic in any C# application.
Real-World Use
Enterprise applications use switch expressions for processing different message types in a service bus. E-commerce systems use pattern matching for applying discount rules based on customer status, order value, and product categories. Game Development uses switch statements for state machines managing game states, player states, and AI behavior.
Learning Path
graph LR
A["06: Operators"] --> B["07: Control Flow"]
B --> C["08: Loops"]
C --> D["09: Classes"]
D --> E["10: Constructors"]
style A fill:#4a90d9,stroke:#2c5f8a,color:#fff
style B fill:#4a90d9,stroke:#2c5f8a,color:#fff
style C fill:#4a90d9,stroke:#2c5f8a,color:#fff
style D fill:#4a90d9,stroke:#2c5f8a,color:#fff
style E fill:#4a90d9,stroke:#2c5f8a,color:#fff
The if/else Statement
int temperature = 30;
if (temperature > 35)
{
Console.WriteLine("Extreme heat warning");
}
else if (temperature > 25)
{
Console.WriteLine("Warm day");
}
else if (temperature > 15)
{
Console.WriteLine("Mild day");
}
else
{
Console.WriteLine("Cold day");
}
// Conditional expression (ternary)
string status = temperature > 30 ? "Hot" : "Normal";
Console.WriteLine(status);
When to use if/else chains
- Complex conditions involving ranges, multiple variables, or method calls
- Conditions that cannot be easily expressed as single values
- Scenarios requiring short-circuit evaluation of expensive operations
Switch Statement
The traditional switch is useful for matching a single value against multiple possibilities:
Console.Write("Enter a day number (1-7): ");
int day = int.Parse(Console.ReadLine());
switch (day)
{
case 1:
Console.WriteLine("Monday");
break;
case 2:
Console.WriteLine("Tuesday");
break;
case 3:
Console.WriteLine("Wednesday");
break;
case 4:
Console.WriteLine("Thursday");
break;
case 5:
Console.WriteLine("Friday");
break;
case 6:
case 7:
Console.WriteLine("Weekend!");
break;
default:
Console.WriteLine("Invalid day");
break;
}
Switch Expression (C# 8+)
Switch expressions provide a more concise, expression-oriented syntax:
string GetDayName(int day) => day switch
{
1 => "Monday",
2 => "Tuesday",
3 => "Wednesday",
4 => "Thursday",
5 => "Friday",
6 or 7 => "Weekend",
_ => "Invalid day"
};
Console.WriteLine(GetDayName(3)); // Wednesday
Console.WriteLine(GetDayName(6)); // Weekend
Key differences from switch statements:
- Uses
=>instead ofcase: break; _replacesdefaultas the discard pattern- The result is an expression (can be assigned or returned)
- No
breakstatements needed
Pattern Matching
Type Pattern
object obj = 42;
if (obj is int number)
{
Console.WriteLine($"It's an integer: {number}");
}
else if (obj is string text)
{
Console.WriteLine($"It's a string: {text}");
}
Property Pattern
record Person(string Name, int Age, string Country);
string CategorizePerson(Person person) => person switch
{
{ Age: < 18 } => "Minor",
{ Age: >= 18, Age: < 65 } => "Adult",
{ Age: >= 65 } => "Senior",
_ => "Unknown"
};
var alice = new Person("Alice", 30, "USA");
Console.WriteLine(CategorizePerson(alice)); // Adult
Tuple Pattern
string GetPlayerAction(string player, string gameState) => (player, gameState) switch
{
("", _) => "No player",
(_, "Paused") => "Waiting",
("Player1", "Playing") => "Player 1's turn",
("Player2", "Playing") => "Player 2's turn",
(_, _) => "Unknown action"
};
Relational Pattern (C# 9+)
string ClassifyNumber(int value) => value switch
{
< 0 => "Negative",
0 => "Zero",
> 0 and < 10 => "Small positive",
>= 10 and < 100 => "Medium positive",
>= 100 => "Large positive"
};
List Pattern (C# 11+)
int[] numbers = { 1, 2, 3 };
string DescribeArray(int[] values) => values switch
{
[] => "Empty",
[var first] => $"Single element: {first}",
[var first, var second] => $"Two elements: {first}, {second}",
[.., var last] => $"Multiple elements, last: {last}"
};
Console.WriteLine(DescribeArray(numbers)); // Multiple elements, last: 3
Best Practices
// Good: Switch expression for value-mapping
string PriorityLabel(int priority) => priority switch
{
1 => "Critical",
2 => "High",
3 => "Medium",
4 => "Low",
_ => "Unknown"
};
// Good: Property pattern for complex matching
decimal CalculateDiscount(Order order) => order switch
{
{ Total: > 1000, Customer.IsVip: true } => 0.20m,
{ Total: > 500 } => 0.10m,
{ Total: > 100 } => 0.05m,
_ => 0m
};
// Bad: Nested ternaries (hard to read)
// var result = a ? (b ? (c ? x : y) : z) : w;
Common Mistakes
Mistake 1: Forgetting break in Switch Statements
Unlike switch expressions, traditional switch statements require break or return after each case. Omitting it causes fall-through (or a compilation error in C#).
Mistake 2: Not Covering All Cases in Switch Expressions
Switch expressions must cover all possible values. The compiler warns if coverage is incomplete. Always include the discard pattern _ as a catch-all.
Mistake 3: Confusing = with == in Conditions
if (x = 5) assigns 5 to x and uses the result as a boolean. In C#, this does not compile unless x is bool. Always use == for comparison.
Mistake 4: Overcomplicating Conditions
if (condition == true) is redundant. Use if (condition). Similarly, if (condition == false) should be if (!condition).
Mistake 5: Nesting Too Deeply
Deeply nested if/else statements are hard to read. Consider early returns, guard clauses, or switch expressions for better readability.
Mistake 6: Using Switch for Simple Boolean Checks
A switch with only two cases (true/false) is overkill. Use an if/else or ternary instead.
Practice Questions
- Write a switch expression that returns the season ("Spring", "Summer", "Fall", "Winter") given a month number (1-12).
- What is the purpose of the discard pattern
_in switch expressions? - How does a property pattern differ from a type pattern in C#?
- Write a method using a tuple pattern to determine the result of a rock-paper-scissors game.
- What is the advantage of switch expressions over switch statements?
Challenge
Create a method CalculateShipping that takes an Order record (with Total, Weight, IsExpress) and uses pattern matching to calculate shipping cost: Free for orders over $100, $5 for orders over $50, $10 standard, or $25 for Express orders under $50.
FAQ
Mini Project
Create an order processing system:
record Customer(string Name, bool IsVip);
record Order(Customer Customer, decimal Total, bool IsInternational);
class OrderProcessor
{
public decimal CalculateShipping(Order order) => order switch
{
{ Total: > 100 } => 0m,
{ Total: > 50, Customer.IsVip: true } => 0m,
{ Total: > 50, IsInternational: true } => 15m,
{ Total: > 50 } => 5m,
{ IsInternational: true } => 25m,
{ Customer.IsVip: true } => 10m,
_ => 15m
};
public string GetShippingEstimate(Order order) => order switch
{
{ Total: > 100 } => "Free shipping (3-5 days)",
{ Customer.IsVip: true } => "Priority (1-2 days)",
{ IsInternational: true } => "International (7-14 days)",
_ => "Standard (3-7 days)"
};
}
var processor = new OrderProcessor();
var orders = new[]
{
new Order(new Customer("Alice", false), 120m, false),
new Order(new Customer("Bob", true), 70m, false),
new Order(new Customer("Charlie", false), 30m, true),
};
foreach (var o in orders)
{
Console.WriteLine($"{o.Customer.Name}: Shipping ${processor.CalculateShipping(o)}");
Console.WriteLine($" {processor.GetShippingEstimate(o)}");
}
Expected output:
Alice: Shipping $0
Free shipping (3-5 days)
Bob: Shipping $0
Priority (1-2 days)
Charlie: Shipping $25
International (7-14 days)
What's Next
You have mastered control flow in C#. The next lesson covers loops: for, foreach, while, do-while, and the break, continue, and yield return statements.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro