← Open in the full interactive course (progress tracking, search & more)

Making decisions and repeating actions — the heart of every program.

Think about the last app you used. Maybe you scrolled through a feed, tapped a button, or saw a personalized message. Every one of those actions involved the program asking itself questions: "Is the user logged in?", "Which item did they tap?", "How many times should I repeat this animation?"

Control flow is the mechanism that lets a program make decisions and repeat actions. Without it, a program would just run from top to bottom, line by line, doing the same thing every time — no choices, no loops, no interactivity.

In this lesson, you'll learn the main control flow statements in C#, how they work, and when to use each one. By the end, you'll be able to write code that branches and loops like a real application.

What Is It?

The Simple Explanation

Control flow is the order in which the statements of a program are executed. It includes branching (choosing between different paths) and looping (repeating a block of code).

The Technical Definition

In C#, control flow statements determine the execution path of a program based on conditions and repetition. The main categories are:

Statement Purpose Example
if Execute block if condition is true if (age >= 18) ...
else if Check another condition if previous false else if (age >= 13) ...
else Execute if all conditions false else ...
switch Select one of many branches based on value/pattern switch (day) { case Mon: ... }
for Repeat a block a known number of times for (int i=0; i<10; i++) ...
foreach Iterate over a collection foreach (var item in items) ...
while Repeat while condition is true while (x < 5) ...
do-while Repeat at least once, then check condition do { ... } while (y > 0);

Why Does It Exist?

The Problem

Programs need to respond to different situations. A payment system must process a credit card differently than a PayPal payment. A game must repeat the game loop until the player quits. A web app must show different pages based on the user's role.

Without control flow, you'd have to write a separate program for every possible scenario — impossible and impractical.

The Solution

Control flow statements allow you to express decision logic and repetition directly in code, making programs dynamic and responsive. They are the building blocks of algorithms.

The key insight

Control flow is about choosing and repeating. Every non-trivial program needs both. Mastering these statements is the first step to writing real applications.

Big Picture

Here's a visual representation of control flow paths in a simple login check:

FLOWCHART: USER LOGIN
Start
Is user authenticated?
Yes ▼
Show dashboard
No ▼
Redirect to login

This simple decision is a perfect example of if-else. Loops would handle repeated tasks, like processing every item in a shopping cart.

How It Works

Let's trace how different control flow statements execute.

Step 1 — if and else

int age = 20;
if (age >= 18)
{
    Console.WriteLine("Adult");
}
else
{
    Console.WriteLine("Minor");
}

The condition age >= 18 is evaluated first. If true, the first block runs; otherwise, the else block runs. Only one branch executes.

Step 2 — else if ladder

int score = 85;
if (score >= 90) Console.WriteLine("A");
else if (score >= 80) Console.WriteLine("B");
else if (score >= 70) Console.WriteLine("C");
else Console.WriteLine("F");

Conditions are checked top to bottom. The first true condition's block executes, and the rest are skipped.

Step 3 — switch statement (classic)

string day = "Monday";
switch (day)
{
    case "Saturday":
    case "Sunday":
        Console.WriteLine("Weekend");
        break;
    case "Monday":
        Console.WriteLine("Start of week");
        break;
    default:
        Console.WriteLine("Weekday");
        break;
}

The switch evaluates day and jumps to the matching case. Multiple cases can share a block. break exits the switch.

Step 4 — switch expression (modern C#)

string dayType = day switch
{
    "Saturday" or "Sunday" => "Weekend",
    "Monday" => "Start of week",
    _ => "Weekday"
};
Console.WriteLine(dayType);

This is a more concise expression form introduced in C# 8. It returns a value and uses pattern matching (or, _ discard).

Step 5 — for loop

for (int i = 0; i < 5; i++)
{
    Console.WriteLine(i);
}

Initialization (int i=0) happens once. Condition (i<5) is checked before each iteration. After each body execution, increment (i++) runs. Loop stops when condition is false.

Step 6 — foreach loop

var numbers = new[] { 1, 2, 3, 4, 5 };
foreach (var n in numbers)
{
    Console.WriteLine(n);
}

foreach iterates over each element in a collection. The loop variable is read-only.

Step 7 — while and do-while

int count = 0;
while (count < 3)
{
    Console.WriteLine(count);
    count++;
}

int attempt = 0;
do
{
    Console.WriteLine("Trying...");
    attempt++;
} while (attempt < 3);

while checks condition before each iteration. do-while executes at least once before checking.

Simple Example

Let's write a small program that categorizes a number as positive, negative, or zero, and then prints numbers from 1 to the absolute value of that number.

using System;

int number = -3;

// Branching
if (number > 0)
{
    Console.WriteLine("Positive");
}
else if (number < 0)
{
    Console.WriteLine("Negative");
}
else
{
    Console.WriteLine("Zero");
}

// Looping - using absolute value to avoid negative bounds
int limit = Math.Abs(number);
for (int i = 1; i <= limit; i++)
{
    Console.WriteLine(i);
}

Code → Meaning → Result

Real-World Example

Consider an e-commerce order processing function that iterates through cart items and applies discounts based on membership tier.

using System;
using System.Collections.Generic;

List<(string Name, decimal Price)> cart = new()
{
    ("Laptop", 1200m),
    ("Mouse", 25m),
    ("Keyboard", 75m)
};

string membership = "Gold";
decimal total = 0m;

foreach (var item in cart)
{
    decimal discountPercent = membership switch
    {
        "Platinum" => 0.20m,
        "Gold" => 0.15m,
        "Silver" => 0.10m,
        _ => 0m
    };

    decimal discount = item.Price * discountPercent;
    decimal finalPrice = item.Price - discount;
    total += finalPrice;

    Console.WriteLine($"{item.Name}: {finalPrice:C}");
}

Console.WriteLine($"Total: {total:C}");

This combines foreach for iteration, switch expression for membership-based discount, and arithmetic operators. The loop accumulates the total.

Why not for? Because we don't need index manipulation; foreach is simpler and more readable for collections.

Analogy

Control Flow as a Recipe

if/else is like a recipe step: "If the dough is sticky, add more flour; otherwise, knead it."

switch is like a vending machine: select a button (case) and get the corresponding snack.

for loop is like counting repetitions: "Do 10 push-ups" — you count from 1 to 10 and stop.

foreach loop is like going through a grocery list: for each item on the list, put it in the cart.

while loop is like stirring until the mixture thickens — you don't know how many times, but you check the condition each time.

do-while is like tasting the soup at least once before deciding if it needs more salt.

This analogy connects control flow to everyday decision-making and repetition, making it intuitive.

Under the Hood

How does the compiler and runtime handle control flow?

INTERNAL VIEW
1. COMPILER TRANSLATION
2. RUNTIME EXECUTION
3. MODERN C# OPTIMIZATIONS
4. EXCEPTION FLOW

Common Confusion

1. else if vs separate if statements

else if is part of a single decision chain: only one branch executes. Separate if statements are independent; multiple can execute.

2. switch statement vs switch expression

3. for vs foreach

Use for when you need an index or need to skip/increment in custom ways. Use foreach when you just need to access each element.

4. while vs do-while

while may execute zero times; do-while executes at least once.

5. break vs continue

break exits the loop entirely. continue skips the rest of the current iteration and moves to the next condition check.

Common Mistakes

Mistake 1 — Forgetting break in classic switch

Without break, execution falls through to the next case, causing unintended behavior.

Always include break or return at the end of each case (unless you intentionally group cases).

Mistake 2 — Off-by-one errors in for loops

for (int i = 0; i <= array.Length; i++) — accesses index out of bounds.

Use i < array.Length to iterate from 0 to Length-1.

Mistake 3 — Infinite loops

while (true) { } without a break or a condition that eventually becomes false.

Ensure the loop condition changes inside the loop, or use break at the right time.

Mistake 4 — Using == to compare strings in if

Actually == works for strings in C#, but it's culture-sensitive in some contexts. For exact ordinal comparison, use string.Equals(a, b, StringComparison.Ordinal).

Mistake 5 — Overcomplicating with nested if

Deeply nested if blocks can be hard to read and maintain.

Consider refactoring to switch expressions, early returns, or guard clauses.

When Should I Use It?

Use if-else when:

Use switch when:

Use for when:

Use foreach when:

Use while / do-while when:

Mental Model

Decision = if / switch
Repetition = for / foreach / while
Escape = break / continue / return

Remember:
· Use foreach for simple collection iteration
· Use for when you need an index
· Prefer switch expressions for value mapping
· Always ensure loops terminate

Key Takeaway


Check Your Understanding

You've seen how control flow statements work, how to choose between them, and common pitfalls. Let's test your ability to reason about them.

1. What is the output of the following code?

int x = 5;
if (x > 10)
    Console.WriteLine("A");
else if (x > 3)
    Console.WriteLine("B");
else
    Console.WriteLine("C");
Show answer

Correct: B

Why B is correct: The first condition x > 10 is false. The next condition x > 3 is true (5 > 3), so "B" is printed. The else block is skipped because the else if branch already matched.

Why A is incorrect: x > 10 is false.

Why C is incorrect: The else branch only runs if all previous conditions are false; here the else if condition was true.

Why D is incorrect: In an if-else if-else chain, only one branch executes.

Reinforcement: Remember: else if is part of a single decision chain; once one condition matches, the rest are skipped.

2. Which loop would you use to iterate over every element of a List<string> when you don't need the index?

Show answer

Correct: C

Why C is correct: foreach is designed for iterating over collections without needing an index. It's simpler and avoids off-by-one errors.

Why A is incorrect: A for loop requires manual index management, which is unnecessary if you just need each element.

Why B is incorrect: while is for condition-based repetition, not direct collection traversal.

Why D is incorrect: do-while is for executing at least once, not for collection iteration.

Reinforcement: Whenever you need to process all items in a collection, foreach is usually the cleanest choice.

3. Which statement about break and continue is correct?

Show answer

Correct: B

Why B is correct: break terminates the loop immediately, while continue skips the remainder of the current iteration and proceeds to the next iteration's condition check (or increment for for loops).

Why A is incorrect: The definitions are reversed.

Why C is incorrect: Only break exits the loop; continue does not.

Why D is incorrect: continue is used inside loops, not switch.

Reinforcement: Remember: break = exit loop; continue = skip to next iteration.

4. What does the following switch expression evaluate to?

int score = 75;
string grade = score switch
{
    >= 90 => "A",
    >= 80 => "B",
    >= 70 => "C",
    _ => "F"
};
Console.WriteLine(grade);
Show answer

Correct: C

Why C is correct: The switch expression uses relational patterns. score is 75. The first pattern >= 90 is false; >= 80 is false; >= 70 is true, so "C" is returned.

Why A is incorrect: 75 is not >= 90.

Why B is incorrect: 75 is not >= 80.

Why D is incorrect: The discard pattern _ only applies if no earlier pattern matches.

Reinforcement: Modern switch expressions support relational patterns and are evaluated in order; the first matching arm wins.

5. Which loop will execute at least once regardless of the condition?

Show answer

Correct: C

Why C is correct: A do-while loop checks the condition after executing the body, guaranteeing at least one iteration.

Why A is incorrect: while checks the condition before entering the loop; if false initially, it runs zero times.

Why B is incorrect: for also checks the condition before the first iteration; it may run zero times.

Why D is incorrect: foreach over an empty collection executes zero times.

Reinforcement: Use do-while when you need the loop body to run at least once, like showing a menu until the user chooses to exit.

You've mastered control flow — now you can make your programs make decisions and repeat actions!


dotnetmadeeasy.com — Learn C# and .NET, the right way.