Chuyển tới nội dung chính

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
Single-statement bodies

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"
};
Prefer switch expressions

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

StatementEffect
breakExits the nearest enclosing loop or switch immediately
continueSkips 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");
Avoid goto

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

ScenarioUse
Two-way branch with simple conditionif / else
Multiple discrete values of a single variableswitch statement
Multi-way branch that produces a valueSwitch expression
Simple two-value inline choiceTernary ? :
Complex Boolean conditionsif / else if chain

Loop Decision Table

ScenarioUse
Known number of iterations (index needed)for
Iterating over a collectionforeach
Unknown iterations; may be zerowhile
Unknown iterations; must run at least oncedo-while

Common Pitfalls

  • C# does NOT allow switch fall-through — Unlike C/C++, every case in a C# switch statement must end with break, return, goto, or throw. 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 with foreach. It throws InvalidOperationException. Collect changes and apply after the loop, or iterate over a copy.

  • Off-by-one errors in for loops — Using <= vs < in the condition. Remember that arrays and lists are zero-indexed: the last valid index is collection.Count - 1.

  • Infinite while loops — Always ensure the loop condition will eventually become false, or include a break statement with a guard.

  • Ternary nesting — Avoid chaining ternary operators (a ? b ? c : d : e). It harms readability — use switch expressions or if/else instead.

Key Takeaways

  1. C# provides if/else, switch statements, switch expressions, and the ternary operator for branching.
  2. Switch expressions (C# 8+) are concise, return values, and support pattern matching — prefer them over verbose switch statements.
  3. Use for for counted iterations, foreach for collections, while for unknown iterations, and do-while when at least one execution is required.
  4. C# prohibits switch fall-through — every case must explicitly exit.
  5. 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 case block to end with an explicit jump statement (break, return, goto, or throw). 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?

break immediately exits the nearest enclosing loop or switch statement. continue skips 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, require break in 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 InvalidOperationException is thrown because foreach uses 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.

References