Skip to content

C# Control Flow — if/else, switch, Switch Expressions, and Pattern Matching

DodaTech Updated 2026-06-28 7 min read

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 of case: break;
  • _ replaces default as the discard pattern
  • The result is an expression (can be assigned or returned)
  • No break statements 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

  1. Write a switch expression that returns the season ("Spring", "Summer", "Fall", "Winter") given a month number (1-12).
  2. What is the purpose of the discard pattern _ in switch expressions?
  3. How does a property pattern differ from a type pattern in C#?
  4. Write a method using a tuple pattern to determine the result of a rock-paper-scissors game.
  5. 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

What is the difference between a switch statement and a switch expression?

A switch statement executes blocks of code for each case. A switch expression returns a value. Switch expressions are more concise and cannot use fall-through behavior.

Can I use pattern matching with any type?

Yes. Pattern matching works with any type in C#. Type patterns work with reference types, value types, and nullable types. Property patterns and tuple patterns work with any accessible members.

What is the `when` clause in pattern matching?

The when clause adds a condition to a pattern: case > 50 when isWeekend:. The pattern matches only when both the pattern and the condition are true.

Are switch expressions faster than switch statements?

Both compile to similar IL. The compiler optimizes switch patterns into efficient lookup tables or binary search trees when possible. Performance differences are typically negligible.

Can I use patterns in if statements?

Yes. The is operator supports type patterns, property patterns, and relational patterns: if (obj is string s && s.Length > 5).

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