C# Control Flow: if, switch, for, foreach and while Loops
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:
- Selection statements —
if,else if,else,switch(classic and modern switch expressions). - Iteration statements —
for,foreach,while,do-while. - Jump statements —
break,continue,return,goto(rarely used). - Exception handling —
try,catch,finally,throw(covered separately).
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.
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:
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
if/else if/elsecategorizes based on sign.Math.Absensures loop runs a positive number of times.forloop repeats exactlylimittimes.
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
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?
if-elsebecomes branch instructions (brtrue,brfalse,br).switchmay be compiled to a jump table (for dense integer cases) or a series of comparisons.forloops become initialization, condition check, body, and increment instructions, with a loop back-edge.foreachuses the enumerator pattern (IEnumerable<T>) or span-based enumeration for arrays.
- The CPU follows branch instructions, jumping to different code sections.
- Loop conditions are re-evaluated each iteration.
- The JIT compiler may optimize loops and branches based on runtime feedback (tiered compilation).
foreachover arrays or spans compiles to efficient index-based access without enumerator allocation.- Pattern matching in
switchis compiled to efficient type tests and property accesses. - C# 14 continues to improve pattern matching and switch expression performance.
- Exceptions alter normal control flow by unwinding the stack.
try-catch-finallyis a separate control flow mechanism covered elsewhere.
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
- Statement: traditional, uses
case,break, can contain statements. - Expression: introduced in C# 8, returns a value, uses
=>, nobreakneeded.
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:
- You have a simple true/false decision.
- You need to check multiple conditions that aren't based on a single value.
- You want to short-circuit with
&&or||.
Use switch when:
- You're comparing the same variable against multiple constant values.
- You can benefit from pattern matching (type, property, relational patterns).
- You want a concise expression that returns a value.
Use for when:
- You know the exact number of iterations.
- You need index access or need to modify the loop variable.
Use foreach when:
- You need to iterate over all elements of a collection.
- You don't need to change the collection or the loop variable.
Use while / do-while when:
- The number of iterations is unknown and depends on a condition.
do-whilewhen you need at least one execution.
Mental Model
if / switchRepetition =
for / foreach / whileEscape =
break / continue / returnRemember:
· 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
- Control flow enables branching and looping in programs.
if,else if,elsehandle conditional execution.switchselects among many branches; modern C# offers expression form with pattern matching.forandforeachrepeat code; choose based on index vs collection iteration.whileanddo-whileloop based on conditions.breakexits loops,continueskips to next iteration.- Pattern matching in
switchandismakes code concise and expressive. - Always ensure loops terminate to avoid infinite loops.
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!