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.
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).
In C#, control flow statements determine the execution path of a program based on conditions and repetition. The main categories are:
if, else if, else, switch (classic and modern switch expressions).for, foreach, while, do-while.break, continue, return, goto (rarely used).try, catch, finally, throw (covered separately).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.
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.
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.
Let's trace how different control flow statements execute.
if and elseint 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.
else if ladderint 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.
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.
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).
for loopfor (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.
foreach loopvar 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.
while and do-whileint 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.
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/else categorizes based on sign.Math.Abs ensures loop runs a positive number of times.for loop repeats exactly limit times.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.
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.
How does the compiler and runtime handle control flow?
if-else becomes branch instructions (brtrue, brfalse, br).switch may be compiled to a jump table (for dense integer cases) or a series of comparisons.for loops become initialization, condition check, body, and increment instructions, with a loop back-edge.foreach uses the enumerator pattern (IEnumerable<T>) or span-based enumeration for arrays.foreach over arrays or spans compiles to efficient index-based access without enumerator allocation.switch is compiled to efficient type tests and property accesses.try-catch-finally is a separate control flow mechanism covered elsewhere.else if vs separate if statementselse if is part of a single decision chain: only one branch executes. Separate if statements are independent; multiple can execute.
switch statement vs switch expressioncase, break, can contain statements.=>, no break needed.for vs foreachUse for when you need an index or need to skip/increment in custom ways. Use foreach when you just need to access each element.
while vs do-whilewhile may execute zero times; do-while executes at least once.
break vs continuebreak exits the loop entirely. continue skips the rest of the current iteration and moves to the next condition check.
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).
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.
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.
== to compare strings in ifActually == works for strings in C#, but it's culture-sensitive in some contexts. For exact ordinal comparison, use string.Equals(a, b, StringComparison.Ordinal).
if Deeply nested if blocks can be hard to read and maintain.
Consider refactoring to switch expressions, early returns, or guard clauses.
if-else when:&& or ||.switch when:for when:foreach when:while / do-while when:do-while when you need at least one execution.if / switchfor / foreach / whilebreak / continue / returnforeach for simple collection iterationfor when you need an indexswitch expressions for value mappingif, else if, else handle conditional execution.switch selects among many branches; modern C# offers expression form with pattern matching.for and foreach repeat code; choose based on index vs collection iteration.while and do-while loop based on conditions.break exits loops, continue skips to next iteration.switch and is makes code concise and expressive.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");
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?
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?
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);
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?
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.