Control Flow
π― What You'll Learn
- How programs make decisions with
if,else if, andelse - Choosing between multiple options with
switchstatements - Modern pattern matching with switch expressions (C# 8+)
- Repeating actions with
for,while,do-while, andforeachloops - Controlling loop execution with
breakandcontinue - Nesting control structures for complex logic
- Common patterns: input validation, menus, searching
What is Control Flow?
So far, our programs have been strictly linearβeach line executes once, in order, from top to bottom. But real programs need to be smarter. They need to:
- Make decisions: "If the user is logged in, show their dashboard; otherwise, show the login page"
- Repeat actions: "For each product in the cart, calculate the line total"
- Skip or exit early: "If we found what we're looking for, stop searching"
Control flow refers to the order in which statements are executed in a program. Control flow statements let you change that orderβbranching to different code paths or repeating sections of code.
Conditional Statements: Making Decisions
Conditional statements execute different code based on whether a condition is true or false.
The most fundamental is the if statement.
The if Statement
The if statement executes a block of code only when a condition is true.
int temperature = 35;
if (temperature > 30)
{
Console.WriteLine("It's hot outside!");
}
// The condition must be a bool (true/false)
bool isLoggedIn = true;
if (isLoggedIn)
{
Console.WriteLine("Welcome back!");
}
// You can use any expression that evaluates to bool
string userRole = "admin";
if (userRole == "admin")
{
Console.WriteLine("You have full access.");
}
The code inside the curly braces { } is called a block.
It only runs if the condition is true. If false, the entire block is skipped.
The if-else Statement
Often you want to do one thing if a condition is true, and something else if it's false.
That's where else comes in.
int age = 16;
if (age >= 18)
{
Console.WriteLine("You can vote.");
}
else
{
Console.WriteLine("You're too young to vote.");
}
// Real-world example: checking stock
int quantityOnHand = 5;
int quantityOrdered = 10;
if (quantityOnHand >= quantityOrdered)
{
Console.WriteLine("Order can be fulfilled.");
quantityOnHand -= quantityOrdered;
}
else
{
Console.WriteLine($"Insufficient stock. Only {quantityOnHand} available.");
}
The if-else if-else Chain
When you have multiple conditions to check, use else if to chain them together.
int score = 85;
string grade;
if (score >= 90)
{
grade = "A";
}
else if (score >= 80)
{
grade = "B";
}
else if (score >= 70)
{
grade = "C";
}
else if (score >= 60)
{
grade = "D";
}
else
{
grade = "F";
}
Console.WriteLine($"Your grade: {grade}"); // "Your grade: B"
Conditions are checked from top to bottom. As soon as one is true, its block executes and all remaining conditions are skipped. Order matters! Put more specific conditions before more general ones.
Nested if Statements
You can place if statements inside other if statements for
more complex logic.
bool isLoggedIn = true;
string userRole = "editor";
bool hasPermission = true;
if (isLoggedIn)
{
if (userRole == "admin")
{
Console.WriteLine("Full access granted.");
}
else if (userRole == "editor" && hasPermission)
{
Console.WriteLine("Editor access granted.");
}
else
{
Console.WriteLine("Read-only access.");
}
}
else
{
Console.WriteLine("Please log in first.");
}
Deeply nested code (3+ levels) becomes hard to read. Consider combining conditions
with && or ||, using early returns, or refactoring into
separate methods (we'll cover this in the Methods section).
Single-Line if (No Braces)
For simple, single-statement blocks, you can omit the curly braces:
int x = 10;
// Without braces (single statement only)
if (x > 5)
Console.WriteLine("x is greater than 5");
// With braces (always works, recommended)
if (x > 5)
{
Console.WriteLine("x is greater than 5");
}
Many developers (and style guides) recommend always using braces, even for single statements. It prevents bugs when you later add more statements and forget to add braces. This is a common source of hard-to-find bugs.
The switch Statement
When you're comparing a single value against many possible options, a switch
statement is often cleaner than a long if-else chain.
Basic switch Syntax
int dayOfWeek = 3;
string dayName;
switch (dayOfWeek)
{
case 1:
dayName = "Monday";
break;
case 2:
dayName = "Tuesday";
break;
case 3:
dayName = "Wednesday";
break;
case 4:
dayName = "Thursday";
break;
case 5:
dayName = "Friday";
break;
case 6:
dayName = "Saturday";
break;
case 7:
dayName = "Sunday";
break;
default:
dayName = "Invalid day";
break;
}
Console.WriteLine(dayName); // "Wednesday"
Key points about switch:
- Each
casemust end withbreak(orreturn,throw, etc.) - The
defaultcase handles any value not matched by other cases - You can switch on: integers, strings, chars, enums, and some other types
Multiple Cases for Same Code
You can stack multiple case labels to run the same code:
int dayOfWeek = 6;
string dayType;
switch (dayOfWeek)
{
case 1:
case 2:
case 3:
case 4:
case 5:
dayType = "Weekday";
break;
case 6:
case 7:
dayType = "Weekend";
break;
default:
dayType = "Invalid";
break;
}
Console.WriteLine(dayType); // "Weekend"
Switching on Strings
string command = "start";
switch (command.ToLower()) // Convert to lowercase for case-insensitive matching
{
case "start":
Console.WriteLine("Starting the process...");
break;
case "stop":
Console.WriteLine("Stopping the process...");
break;
case "restart":
Console.WriteLine("Restarting...");
break;
case "status":
Console.WriteLine("Checking status...");
break;
default:
Console.WriteLine($"Unknown command: {command}");
break;
}
Switch with Pattern Matching (C# 7+)
Modern C# allows powerful pattern matching in switch statements:
// Type pattern matching
object value = 42;
switch (value)
{
case int n:
Console.WriteLine($"It's an integer: {n}");
break;
case string s:
Console.WriteLine($"It's a string: {s}");
break;
case null:
Console.WriteLine("It's null");
break;
default:
Console.WriteLine("Unknown type");
break;
}
// Pattern matching with conditions (when clause)
int number = 15;
switch (number)
{
case int n when n < 0:
Console.WriteLine("Negative");
break;
case int n when n == 0:
Console.WriteLine("Zero");
break;
case int n when n > 0 && n <= 10:
Console.WriteLine("Small positive");
break;
case int n when n > 10:
Console.WriteLine("Large positive");
break;
}
Switch Expressions (C# 8+)
Switch expressions are a more concise way to write switches that return a value. This is modern C# at its best:
int dayOfWeek = 3;
// Switch expression - concise and returns a value
string dayName = dayOfWeek switch
{
1 => "Monday",
2 => "Tuesday",
3 => "Wednesday",
4 => "Thursday",
5 => "Friday",
6 => "Saturday",
7 => "Sunday",
_ => "Invalid day" // _ is the discard pattern (like default)
};
// With pattern matching
int score = 85;
string grade = score switch
{
>= 90 => "A",
>= 80 => "B",
>= 70 => "C",
>= 60 => "D",
_ => "F"
};
// Multiple values with or pattern
string dayType = dayOfWeek switch
{
1 or 2 or 3 or 4 or 5 => "Weekday",
6 or 7 => "Weekend",
_ => "Invalid"
};
// With range pattern
string ageGroup = age switch
{
< 0 => "Invalid",
< 13 => "Child",
< 20 => "Teenager",
< 65 => "Adult",
_ => "Senior"
};
When you need to assign a value based on a condition, switch expressions are cleaner and more concise than traditional switch statements. They're also harder to mess up because the compiler ensures all cases return a value.
Loops: Repeating Actions
Loops let you execute a block of code multiple times. C# provides four types of loops, each suited for different scenarios.
The for Loop
The for loop is best when you know exactly how many times you
want to repeat something.
// Basic for loop: count from 0 to 4
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"Iteration {i}");
}
// Output: Iteration 0, Iteration 1, Iteration 2, Iteration 3, Iteration 4
// Anatomy of a for loop:
// for (initialization; condition; update)
// initialization: runs once before the loop starts (int i = 0)
// condition: checked before each iteration (i < 5)
// update: runs after each iteration (i++)
// Count from 1 to 10
for (int i = 1; i <= 10; i++)
{
Console.WriteLine(i);
}
// Count backwards
for (int i = 10; i >= 1; i--)
{
Console.WriteLine(i);
}
// Count by twos
for (int i = 0; i <= 20; i += 2)
{
Console.WriteLine(i); // 0, 2, 4, 6, ..., 20
}
// Real example: calculate sum of 1 to 100
int sum = 0;
for (int i = 1; i <= 100; i++)
{
sum += i;
}
Console.WriteLine($"Sum: {sum}"); // Sum: 5050
The while Loop
The while loop repeats as long as a condition is true. Use it when you
don't know in advance how many iterations you'll need.
// Basic while loop
int count = 0;
while (count < 5)
{
Console.WriteLine($"Count: {count}");
count++;
}
// Real example: input validation
string input = "";
while (string.IsNullOrWhiteSpace(input))
{
Console.Write("Enter your name: ");
input = Console.ReadLine() ?? "";
}
Console.WriteLine($"Hello, {input}!");
// Example: keep halving until less than 1
double value = 100.0;
int iterations = 0;
while (value >= 1)
{
value /= 2;
iterations++;
}
Console.WriteLine($"Took {iterations} iterations"); // 7 iterations
A while loop runs forever if its condition never becomes false. Always
ensure something inside the loop eventually makes the condition false. If your program
seems to hang, you might have an infinite loop (press Ctrl+C to stop it).
The do-while Loop
The do-while loop is like while, but it checks the condition
after each iteration. This guarantees at least one execution.
// do-while always executes at least once
int number = 10;
do
{
Console.WriteLine($"Number: {number}");
number--;
} while (number > 0);
// Perfect for menu systems!
int choice;
do
{
Console.WriteLine("\n=== InvenTrack Menu ===");
Console.WriteLine("1. View Products");
Console.WriteLine("2. Add Product");
Console.WriteLine("3. Update Stock");
Console.WriteLine("4. Exit");
Console.Write("Enter choice: ");
if (int.TryParse(Console.ReadLine(), out choice))
{
switch (choice)
{
case 1:
Console.WriteLine("Showing products...");
break;
case 2:
Console.WriteLine("Adding product...");
break;
case 3:
Console.WriteLine("Updating stock...");
break;
case 4:
Console.WriteLine("Goodbye!");
break;
default:
Console.WriteLine("Invalid choice.");
break;
}
}
else
{
Console.WriteLine("Please enter a number.");
choice = 0; // Reset to continue loop
}
} while (choice != 4);
The foreach Loop
The foreach loop iterates over every element in a collection. It's the
cleanest way to process arrays, lists, and other collections.
// Iterating over an array
string[] fruits = { "Apple", "Banana", "Cherry", "Date" };
foreach (string fruit in fruits)
{
Console.WriteLine(fruit);
}
// Iterating over a string (characters)
string word = "Hello";
foreach (char letter in word)
{
Console.WriteLine(letter); // H, e, l, l, o
}
// Real example: calculating cart total
decimal[] prices = { 19.99m, 29.99m, 9.99m, 49.99m };
decimal total = 0m;
foreach (decimal price in prices)
{
total += price;
}
Console.WriteLine($"Total: {total:C}"); // Total: $109.96
// Using var for type inference
foreach (var price in prices)
{
Console.WriteLine(price);
}
You cannot modify the collection while iterating with foreach. The loop
variable (fruit, price) is read-only. If you need to modify
elements, use a regular for loop with index access.
Choosing the Right Loop
| Loop Type | Best For | Example Use Case |
|---|---|---|
for |
Known number of iterations, need index | Process items 0-99, access array by index |
while |
Unknown iterations, condition-based | Read until end of file, wait for user input |
do-while |
Must run at least once | Menu systems, input validation |
foreach |
Process every item in a collection | Sum prices, display all items, search |
Loop Control: break and continue
Sometimes you need to interrupt a loop's normal flowβexit early or skip to the next
iteration. That's where break and continue come in.
break: Exit the Loop Immediately
// Search for a value - stop when found
int[] numbers = { 4, 8, 15, 16, 23, 42 };
int searchFor = 16;
bool found = false;
foreach (int number in numbers)
{
if (number == searchFor)
{
found = true;
break; // Exit loop immediately
}
Console.WriteLine($"Checked {number}");
}
Console.WriteLine(found ? "Found!" : "Not found");
// Output: Checked 4, Checked 8, Checked 15, Found!
// Breaking out of a while loop
int attempts = 0;
const int MaxAttempts = 3;
while (true) // Infinite loop - we'll break out manually
{
Console.Write("Enter password: ");
string password = Console.ReadLine() ?? "";
if (password == "secret123")
{
Console.WriteLine("Access granted!");
break; // Correct password - exit loop
}
attempts++;
if (attempts >= MaxAttempts)
{
Console.WriteLine("Too many attempts. Account locked.");
break; // Too many attempts - exit loop
}
Console.WriteLine($"Wrong password. {MaxAttempts - attempts} attempts remaining.");
}
continue: Skip to the Next Iteration
// Skip negative numbers
int[] numbers = { 1, -2, 3, -4, 5, -6, 7 };
int sum = 0;
foreach (int number in numbers)
{
if (number < 0)
{
continue; // Skip this iteration, go to next number
}
sum += number;
}
Console.WriteLine($"Sum of positives: {sum}"); // 16
// Skip empty lines when processing
string[] lines = { "Hello", "", "World", " ", "!" };
foreach (string line in lines)
{
if (string.IsNullOrWhiteSpace(line))
{
continue; // Skip blank lines
}
Console.WriteLine($"Processing: {line}");
}
// Output: Processing: Hello, Processing: World, Processing: !
// Real example: process only active products
foreach (var product in products)
{
if (!product.IsActive)
{
continue; // Skip inactive products
}
// Process active product...
Console.WriteLine($"Processing {product.Name}");
}
break β Exits the loop completely. Code after the loop runs next.
continue β Skips the rest of the current iteration. The loop continues with the next iteration.
Nested Loops
You can place loops inside other loops. This is useful for working with multi-dimensional data like grids, tables, or matrices.
// Multiplication table
for (int row = 1; row <= 5; row++)
{
for (int col = 1; col <= 5; col++)
{
Console.Write($"{row * col,4}"); // Right-align in 4 characters
}
Console.WriteLine(); // New line after each row
}
/*
Output:
1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
5 10 15 20 25
*/
// Breaking out of nested loops (using a flag)
bool found = false;
for (int i = 0; i < 10 && !found; i++)
{
for (int j = 0; j < 10 && !found; j++)
{
if (grid[i, j] == target)
{
Console.WriteLine($"Found at [{i},{j}]");
found = true;
}
}
}
Putting It All Together: InvenTrack Stock Checker
Let's apply everything we've learned to a practical scenarioβchecking inventory and making decisions about stock levels:
// Product data (we'll use proper classes later)
string[] productNames = { "Widget A", "Widget B", "Gadget X", "Gadget Y", "Tool Z" };
int[] quantities = { 150, 8, 0, 45, 12 };
int[] reorderLevels = { 25, 10, 5, 20, 15 };
decimal[] prices = { 19.99m, 29.99m, 49.99m, 39.99m, 89.99m };
bool[] isActive = { true, true, false, true, true };
// Counters for summary
int outOfStockCount = 0;
int lowStockCount = 0;
int healthyStockCount = 0;
decimal totalInventoryValue = 0m;
Console.WriteLine("ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ");
Console.WriteLine("β INVENTRAK STOCK STATUS REPORT β");
Console.WriteLine("ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ");
Console.WriteLine();
// Process each product
for (int i = 0; i < productNames.Length; i++)
{
// Skip inactive products
if (!isActive[i])
{
Console.WriteLine($"[INACTIVE] {productNames[i]} - Skipped");
continue;
}
// Determine stock status
string status;
string action;
if (quantities[i] == 0)
{
status = "π΄ OUT OF STOCK";
action = "URGENT: Order immediately!";
outOfStockCount++;
}
else if (quantities[i] <= reorderLevels[i])
{
status = "π‘ LOW STOCK";
action = "Reorder soon";
lowStockCount++;
}
else
{
status = "π’ IN STOCK";
action = "No action needed";
healthyStockCount++;
}
// Calculate inventory value
decimal productValue = quantities[i] * prices[i];
totalInventoryValue += productValue;
// Display product info
Console.WriteLine($"Product: {productNames[i]}");
Console.WriteLine($" Quantity: {quantities[i]} (Reorder at: {reorderLevels[i]})");
Console.WriteLine($" Value: {productValue:C}");
Console.WriteLine($" Status: {status}");
Console.WriteLine($" Action: {action}");
Console.WriteLine();
}
// Summary using switch expression
string overallStatus = outOfStockCount switch
{
> 2 => "CRITICAL - Multiple products out of stock!",
> 0 => "WARNING - Some products need attention",
_ when lowStockCount > 0 => "CAUTION - Low stock items present",
_ => "HEALTHY - All products well stocked"
};
Console.WriteLine("ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ");
Console.WriteLine("SUMMARY");
Console.WriteLine("ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ");
Console.WriteLine($" π’ Healthy Stock: {healthyStockCount} products");
Console.WriteLine($" π‘ Low Stock: {lowStockCount} products");
Console.WriteLine($" π΄ Out of Stock: {outOfStockCount} products");
Console.WriteLine($" π° Total Value: {totalInventoryValue:C}");
Console.WriteLine();
Console.WriteLine($" Overall: {overallStatus}");
Key Takeaways
- if/else if/else executes code based on boolean conditions
- switch statements are cleaner than long if-else chains when comparing one value to many options
- Switch expressions (C# 8+) are concise when you need to return a value based on a condition
- for loops are best when you know how many iterations you need
- while loops repeat as long as a condition is true (check first)
- do-while loops guarantee at least one execution (check after)
- foreach loops iterate over every item in a collection
- break exits a loop immediately; continue skips to the next iteration
- Always use braces
{ }even for single-statement blocks - Avoid deep nestingβconsider combining conditions or refactoring
You can now make decisions and repeat actions. In the next section, we'll learn how to organize code into reusable methodsβthe building blocks of well-structured programs.
What's Next?
In the next section, Methods and Functions, we'll learn how to break our code into reusable pieces. You'll understand parameters, return values, method overloading, and how methods make code more organized and maintainable.