Control Flow in C#
Learn how to direct program execution in C# with if/else conditionals, switch statements and expressions, loops, and loop control, with runnable Docker examples
Control flow is how a program decides what to do and how many times to do it. Without it, code would run top to bottom exactly once with no decisions and no repetition. C# gives you the full toolbox the C family is known for — if/else, switch, for, while, do/while — and then layers modern, expression-oriented constructs on top, most notably the switch expression and pattern matching that arrived with C# 8 and have grown in every release since.
As a multi-paradigm language, C# lets you choose your style. You can write imperative, statement-based control flow that looks almost identical to C or Java, or you can lean into the functional side with expressions that return values instead of mutating state. The switch keyword captures this duality perfectly: there is a classic switch statement that executes branches, and a switch expression that evaluates to a result. Both are idiomatic; which you reach for depends on whether you want a side effect or a value.
Because C# is statically and strongly typed, the compiler checks your conditions and patterns ahead of time. A switch over an enum can warn you about unhandled cases, an if condition must actually be a bool (no truthy integers as in C), and pattern matches are verified for type compatibility. In this tutorial we will work through conditionals, both flavors of switch, the loop family, and loop control with break and continue — each as a complete, runnable program.
Conditionals: if, else if, else
The if statement is the most fundamental branch. Its condition must evaluate to a bool — unlike C, you cannot use a bare integer as a truth value. You can chain conditions with else if, fall through to a final else, and combine boolean tests with the short-circuiting && and || operators. For simple either/or value selection, the ternary ?: operator gives you a compact conditional expression.
Create a file named Program.cs:
| |
Run it:
| |
Expected output:
Comfortable
The office is open
Score 85: Pass
The switch Statement
When you need to branch on many discrete values of a single expression, a switch statement is clearer than a long else if chain. Each case label is followed by statements and must end with break (C# does not allow accidental fall-through between non-empty cases). You can stack multiple labels to share a body, and default catches anything unmatched.
Create a file named Program.cs:
| |
Run it with the same Docker command shown above. Expected output:
Day 3 is Wednesday
The switch Expression and Pattern Matching
Modern C# (8.0+) adds the switch expression, which evaluates to a value rather than executing statements. It is more concise, requires every arm to produce a result, and supports rich patterns: relational patterns (< 10), the discard _ for the default arm, when guards, and tuple patterns that match several values at once. This is C#’s functional side showing through.
Create a file named Program.cs:
| |
Run it with the same Docker command. Expected output:
7 -> small
42 -> medium
500 -> large
Player A wins
Tie
Player B wins
Loops: for, while, do-while, and foreach
C# offers four loop forms. The for loop is ideal when you know the iteration count and need an index. The while loop runs as long as a condition holds, checking before each pass. The do/while loop checks after each pass, so its body always runs at least once. And foreach iterates directly over the elements of any collection without an index — the most common and readable choice when you just need each item.
Create a file named Program.cs:
| |
Run it with the same Docker command. Expected output:
for loop (squares):
1 squared is 1
2 squared is 4
3 squared is 9
4 squared is 16
5 squared is 25
while loop (countdown):
3...
2...
1...
Liftoff!
do-while loop:
n = 10
foreach loop:
C#
F#
VB.NET
Notice the do/while loop printed n = 10 even though 10 < 10 is false — the body runs before the condition is checked.
Loop Control: break and continue
Inside any loop, continue skips the rest of the current iteration and jumps to the next one, while break exits the loop entirely. They let you express “skip this case” and “I’m done early” without contorting your loop condition.
Create a file named Program.cs:
| |
Run it with the same Docker command. Expected output:
Odd numbers, stopping once we pass 7:
1
3
5
7
Done.
Running with Docker
Each example in this tutorial is a complete program. Save it as Program.cs and run it with:
| |
The command preserves your Program.cs, scaffolds a temporary project around it, compiles, and runs it — no local .NET SDK required.
Key Concepts
- Conditions must be
boolin C#. Unlike C, you cannot use a bare integer or null as a truth value — this catches a whole class of bugs at compile time. - The
switchstatement branches on discrete values and requiresbreakto end each non-empty case; there is no accidental fall-through. Stackcaselabels to share a body. - The
switchexpression (C# 8+) returns a value and supports rich patterns — relational (< 10), tuple, andwhenguards — bringing a functional style to branching. - Arms are evaluated top to bottom, so order your patterns from most specific to most general and end with the discard
_to catch everything else. - Four loop forms cover every need:
for(indexed, known count),while(check before),do/while(check after, runs at least once), andforeach(iterate a collection directly). foreachis the idiomatic choice for walking a collection when you don’t need the index — it reads clearly and avoids off-by-one errors.continueskips to the next iteration andbreakexits the loop entirely, letting you keep loop conditions simple.- The ternary
?:operator is a compact conditional expression for either/or value selection, complementing statement-basedif/else.
Running Today
All examples can be run using Docker:
docker pull mcr.microsoft.com/dotnet/sdk:9.0
Comments
Loading comments...
Leave a Comment