Control Flow
Definition
Control flow refers to the order in which individual statements, instructions, or function calls are executed in a program. C# provides conditional statements (if, switch) for branching, loops (for, foreach, while, do-while) for iteration, and jump statements (break, continue, return) for transferring control.
Core Concepts
if / else if / else
The if statement evaluates a Boolean expression and executes a block conditionally.
int score = 85;
if (score >= 90)
{
Console.WriteLine("Grade: A");
}
else if (score >= 80)
{
Console.WriteLine("Grade: B");
}
else if (score >= 70)
{
Console.WriteLine("Grade: C");
}
else
{
Console.WriteLine("Grade: F");
}
// Output: Grade: B
When the body is a single statement, braces are technically optional but always use braces for readability and to prevent bugs when adding lines later.
switch Statement
The switch statement selects a section to execute from a list of candidates based on a pattern match.
Traditional switch:
string dayOfWeek = DateTime.Now.DayOfWeek.ToString();
switch (dayOfWeek)
{
case "Monday":
case "Tuesday":
case "Wednesday":
case "Thursday":
case "Friday":
Console.WriteLine("Weekday");
break; // C# requires explicit break
case "Saturday":
case "Sunday":
Console.WriteLine("Weekend");
break;
default:
Console.WriteLine("Unknown");
break;
}
Switch with pattern matching (C# 7+):
object value = 42;
switch (value)
{
case int i when i > 0:
Console.WriteLine($"Positive integer: {i}");
break;
case string s:
Console.WriteLine($"String of length {s.Length}");
break;
case null:
Console.WriteLine("Null value");
break;
default:
Console.WriteLine("Something else");
break;
}
Switch Expressions (C# 8+)
Switch expressions are more concise and return a value. They use pattern matching with the => arrow syntax.
string grade = score switch
{
>= 90 => "A",
>= 80 => "B",
>= 70 => "C",
>= 60 => "D",
_ => "F" // _ is the discard pattern (default)
};
With tuple patterns:
string Classify(int x, int y) => (x, y) switch
{
(0, 0) => "Origin",
(> 0, > 0) => "Quadrant I",
(< 0, > 0) => "Quadrant II",
(< 0, < 0) => "Quadrant III",
(> 0, < 0) => "Quadrant IV",
(0, _) or (_, 0) => "On an axis"
};
Switch expressions are more concise, expression-oriented, and less error-prone than switch statements. Use them when you need to produce a value from a multi-way branch.
Ternary Operator (? :)
A compact inline conditional that returns one of two values.
int age = 20;
string category = age >= 18 ? "Adult" : "Minor";
// Equivalent to:
string category;
if (age >= 18)
category = "Adult";
else
category = "Minor";
Loops
for Loop
Use when you know the number of iterations in advance.
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"Iteration {i}");
}
foreach Loop
Use to iterate over collections implementing IEnumerable.
var fruits = new List<string> { "Apple", "Banana", "Cherry" };
foreach (var fruit in fruits)
{
Console.WriteLine(fruit);
}
while Loop
Use when the number of iterations is unknown — check condition before each iteration.
int attempts = 0;
while (attempts < 3)
{
if (TryConnect())
break;
attempts++;
}
do-while Loop
Use when the body must execute at least once — condition is checked after.
string input;
do
{
Console.Write("Enter 'yes' to continue: ");
input = Console.ReadLine();
} while (input != "yes");
break and continue
| Statement | Effect |
|---|---|
break | Exits the nearest enclosing loop or switch immediately |
continue | Skips the rest of the current iteration and moves to the next |
for (int i = 0; i < 10; i++)
{
if (i == 3) continue; // Skip 3
if (i == 7) break; // Stop at 7
Console.Write($"{i} "); // Output: 0 1 2 4 5 6
}
goto
goto Label;
// ...
Label:
Console.WriteLine("Jumped here");
goto exists in C# but is rarely needed. Its only common legitimate use is transferring control within a switch statement. Prefer structured control flow (loops, break, methods) for clarity and maintainability.
Code Examples
Comprehensive Switch Expression
public enum HttpStatus { Ok, NotFound, ServerError, Unauthorized }
public static string GetMessageType(HttpStatus status) => status switch
{
HttpStatus.Ok => "Success",
HttpStatus.NotFound => "Resource not found",
HttpStatus.ServerError => "Internal error",
HttpStatus.Unauthorized => "Access denied",
_ => "Unknown status"
};
Nested Loop with break and continue
var matrix = new int[,] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
for (int row = 0; row < matrix.GetLength(0); row++)
{
for (int col = 0; col < matrix.GetLength(1); col++)
{
if (matrix[row, col] == 5) continue; // Skip 5
Console.Write($"{matrix[row, col]} ");
}
Console.WriteLine();
}
Looping with Enumeration
foreach (var (item, index) in fruits.Select((f, i) => (f, i)))
{
Console.WriteLine($"[{index}] {item}");
}
When to Use
Branching Decision Table
| Scenario | Use |
|---|---|
| Two-way branch with simple condition | if / else |
| Multiple discrete values of a single variable | switch statement |
| Multi-way branch that produces a value | Switch expression |
| Simple two-value inline choice | Ternary ? : |
| Complex Boolean conditions | if / else if chain |
Loop Decision Table
| Scenario | Use |
|---|---|
| Known number of iterations (index needed) | for |
| Iterating over a collection | foreach |
| Unknown iterations; may be zero | while |
| Unknown iterations; must run at least once | do-while |
Common Pitfalls
-
C# does NOT allow switch fall-through — Unlike C/C++, every
casein a C#switchstatement must end withbreak,return,goto, orthrow. Empty cases that share a body (e.g.,case "Monday": case "Tuesday": ...) are allowed because they are not true fall-through. -
Modifying a collection during
foreach— You cannot add or remove elements from a collection while iterating withforeach. It throwsInvalidOperationException. Collect changes and apply after the loop, or iterate over a copy. -
Off-by-one errors in
forloops — Using<=vs<in the condition. Remember that arrays and lists are zero-indexed: the last valid index iscollection.Count - 1. -
Infinite
whileloops — Always ensure the loop condition will eventually becomefalse, or include abreakstatement with a guard. -
Ternary nesting — Avoid chaining ternary operators (
a ? b ? c : d : e). It harms readability — useswitchexpressions orif/elseinstead.
Key Takeaways
- C# provides
if/else,switchstatements, switch expressions, and the ternary operator for branching. - Switch expressions (C# 8+) are concise, return values, and support pattern matching — prefer them over verbose switch statements.
- Use
forfor counted iterations,foreachfor collections,whilefor unknown iterations, anddo-whilewhen at least one execution is required. - C# prohibits switch fall-through — every case must explicitly exit.
- Never modify a collection while iterating with
foreach.
Interview Questions
Q: Does C# support switch fall-through?
No. Unlike C and C++, C# requires every
caseblock to end with an explicit jump statement (break,return,goto, orthrow). Empty cases that share a body are allowed (e.g.,case 1: case 2: ... break;), but this is not fall-through — the cases are grouped.
Q: What is the difference between break and continue?
breakimmediately exits the nearest enclosing loop orswitchstatement.continueskips the remaining code in the current iteration and proceeds to the next iteration of the loop. Neither affects outer loops.
Q: How do switch expressions differ from switch statements?
Switch expressions (C# 8+) are expressions — they return a value and use
=>arrow syntax. They are more concise, support pattern matching naturally, and use_as the discard/default pattern. Switch statements are statement-based, requirebreakin each case, and do not return a value directly. Switch expressions cannot contain statements in their arms.
Q: What happens if you modify a collection during a foreach loop?
An
InvalidOperationExceptionis thrown becauseforeachuses an enumerator, and enumerators become invalid when the underlying collection is modified. To work around this, iterate over a copy of the collection (e.g.,foreach (var item in list.ToList())), or collect changes and apply them after the loop.