C# · Beginner
Conditions and Loops in C#
In short: C# executes statements top to bottom unless a condition or a loop redirects it. if, else if and else choose a block from a bool expression, switch selects one of many cases, for repeats a counted number of times, while repeats while a condition holds, do-while checks after each pass, foreach visits every element of a collection, and break and continue leave a loop or skip to.
Choosing and repeating
A condition in C# must be a bool. if (count) does not compile when count is an int; you write if (count > 0). The comparison operators ==, !=, <, <=, > and >= produce bools, and && (and), || (or) and ! (not) combine them. && and || short-circuit: the right side is evaluated only if the left side has not already decided the answer, so line != null && line.Length > 0 is safe to write in that order.
if runs its block when the condition is true; else if tests another condition only when the ones before it were false; else catches everything left. Exactly one branch of a chain runs, and the chain stops at the first true condition, so put the most specific test first. Braces are optional around a single statement, but always writing them prevents the classic bug where a second statement is indented under an if yet runs unconditionally. The conditional operator condition ? a : b picks a value rather than a statement and is handy inside an assignment or an interpolated string.
switch compares one value against several cases. In the statement form each case ends with break (or return); C# does not let execution fall from one case with statements into the next, which removes a whole class of bugs known from C and JavaScript. Since C# 8 there is also a switch expression, value switch { pattern => result, ... }, which produces a value and supports patterns such as < 20 (a relational pattern, C# 9) and _ for anything else. Use the expression form when every branch computes a value of the same type.
for (initialiser; condition; step) is the counted loop: the initialiser runs once, the condition is checked before every pass, and the step runs after each pass. for (int i = 0; i < n; i++) runs exactly n times with i from 0 to n - 1, which is also how you walk an array by index. while (condition) repeats as long as the condition holds and is the right choice when the number of passes is unknown, such as reading input until it ends. do { } while (condition); runs the body at least once and checks afterwards, which suits asking until the answer is valid.
foreach (var item in collection) visits every element of an array, list or string in order, without an index and without the chance of an off-by-one error; use it whenever you do not need the position. break leaves the innermost loop immediately; continue skips the rest of the current pass and goes to the next condition check. Both apply only to the loop they sit in, so leaving two nested loops needs a flag or a return from a method.
Reading all of standard input is a pattern you will write constantly: while ((line = Console.ReadLine()) != null) assigns the next line and stops when ReadLine returns null at the end of the input. Combined with break for a sentinel value such as stop and continue for lines to ignore, it handles any number of lines with no count up front.
Syntax
if (condition)
{
// runs when condition is true
}
else if (otherCondition)
{
// runs when the first was false and this one is true
}
else
{
// runs otherwise
}
switch (value)
{
case 1:
// ...
break;
case 2:
case 3: // two labels, one body
// ...
break;
default:
break;
}
string size = value switch // switch expression, C# 8 and later
{
< 20 => "small", // relational pattern, C# 9 and later
< 50 => "medium",
_ => "large"
};
for (int i = 0; i < count; i++) { } // counted
while (condition) { } // check first
do { } while (condition); // run once, then check
foreach (int x in numbers) { } // every element in orderif, else if and else inside a counted loop
The first line says how many days follow; each day's parcel count is classified.
int days = int.Parse(Console.ReadLine()!);
for (int day = 1; day <= days; day++)
{
int parcels = int.Parse(Console.ReadLine()!);
if (parcels == 0)
{
Console.WriteLine($"Day {day}: no parcels");
}
else if (parcels < 10)
{
Console.WriteLine($"Day {day}: light ({parcels})");
}
else
{
Console.WriteLine($"Day {day}: heavy ({parcels})");
}
}Input given to the program: 4 ↵ 0 ↵ 7 ↵ 15 ↵ 10
Output
Day 1: no parcels Day 2: light (7) Day 3: heavy (15) Day 4: heavy (10)
The for loop runs days times, and day counts from 1 so the output reads naturally; the condition day <= days makes the last pass the one where day equals days. Each pass reads one more line and sends it down the chain: 0 matches the first test, 7 fails it and matches < 10, and 15 and 10 reach else. Note that 10 is not < 10, so it counts as heavy. The boundary is decided by the comparison you write, and trying a value exactly on the edge is the quickest way to catch an off-by-one.
while until the input ends, with break, continue and switch
Readings arrive one per line; stop ends them early, negatives are skipped, and the total is described in two ways.
int total = 0;
int skipped = 0;
string? line;
while ((line = Console.ReadLine()) != null)
{
if (line == "stop")
{
break;
}
int value = int.Parse(line);
if (value < 0)
{
skipped++;
continue;
}
total += value;
}
Console.WriteLine($"Total {total}, skipped {skipped}");
string size = total switch
{
< 20 => "small",
< 50 => "medium",
_ => "large"
};
Console.WriteLine(size);
switch (skipped)
{
case 0:
Console.WriteLine("Every reading counted");
break;
case 1:
Console.WriteLine("One reading skipped");
break;
default:
Console.WriteLine("Several readings skipped");
break;
}Input given to the program: 12 ↵ -3 ↵ 20 ↵ 5 ↵ stop ↵ 99
Output
Total 37, skipped 1 medium One reading skipped
The while header reads a line and keeps going until ReadLine returns null, but stop triggers break first, so the 99 after it is never read. The line -3 hits continue, which jumps straight to the next read without reaching total += value. The switch expression turns the total 37 into medium because its patterns are tested in order: < 20 fails, then < 50 succeeds. The switch statement on skipped shows the case and break form with a default for every other value.
Common mistakes
Using = instead of == in a condition
Why it goes wrong:
if (count = 5)assigns 5 and then tries to use an int as the condition, which fails witherror CS0029: Cannot implicitly convert type 'int' to 'bool'. C# never treats a number as true or false.Fix: Compare with
==. To test a bool, name it directly:if (isOpen).Forgetting break at the end of a switch case
Why it goes wrong: Execution cannot fall from a case with statements into the next one; the compiler reports
error CS0163: Control cannot fall through from one case label to another.Fix: End every case with
break,returnorthrow. To give several values one body, stack the labels:case 2: case 3:.Off-by-one in a counted loop
Why it goes wrong:
for (int i = 1; i < n; i++)runs n - 1 times, andfor (int i = 0; i <= n; i++)runs n + 1 times. Both look right at a glance.Fix: Start at 0 with
<, or start at 1 with<=, and walk through the loop in your head with the smallest input, n = 1.C# · fixfor (int i = 0; i < n; i++) // n passes: 0 .. n - 1 for (int i = 1; i <= n; i++) // n passes: 1 .. nA while whose condition never changes
Why it goes wrong:
while (line != "stop")with the only read placed before the loop runs forever, because nothing inside the body updatesline.Fix: Make sure the body changes something the condition depends on, or move the read into the condition as in the example above.
Which loop to use
| Loop | Checks the condition | Use it when |
|---|---|---|
| for | before each pass | you know how many passes there are, or you need the index |
| while | before each pass | passes continue until something happens, such as the input ending |
| do-while | after each pass | the body must run at least once, such as asking until the answer is valid |
| foreach | not a condition: visits each element | you want every element of a collection and no index |
Where you use this
Validating what a person typed is where conditions and loops meet. A program that asks for a quantity should keep asking until it gets a positive whole number, which is a do-while around int.TryParse and a range check. In batch processing the loop is instead a while over every line of a file, with continue to skip blank or comment lines and break on an end marker. Once a line is accepted, an if chain or a switch decides what it means. Almost every exercise on this site is one of these two loops wrapped around a condition.
string? text;
int quantity;
do
{
text = Console.ReadLine();
if (text == null)
{
return; // input ended: give up
}
}
while (!int.TryParse(text, out quantity) || quantity <= 0);
Console.WriteLine($"Accepted {quantity}");Key points
- Conditions must be bool; numbers are never treated as true or false.
- An if / else if / else chain runs exactly one branch: the first whose condition is true.
switchcases end withbreak; the switch expression (C# 8) returns a value and supports patterns.forwhen the count or the index matters,whilewhen it does not,do-whileto run at least once,foreachfor every element.breakleaves the loop andcontinueskips to the next pass; both affect only the innermost loop.while ((line = Console.ReadLine()) != null)reads input until it ends.
Try it yourself
The program prints the numbers from 1 to n. Change the loop so that it skips every number divisible by 4 (use continue) and stops entirely before printing any number greater than 10 (use break), whatever n is.
int n = int.Parse(Console.ReadLine()!);
for (int i = 1; i <= n; i++)
{
Console.WriteLine(i);
}
201
2
3
5
6
7
9
10int n = int.Parse(Console.ReadLine()!);
for (int i = 1; i <= n; i++)
{
if (i > 10)
{
break;
}
if (i % 4 == 0)
{
continue;
}
Console.WriteLine(i);
}
Practise this
- Greenhouse alertEasy
Read a greenhouse temperature and print which action the controller takes. Practise if, else if and else with inclusive ranges in C#.
Control flowNot started
- Staircase step counterEasy
Print a running total of steps climbed after each flight of stairs. A C# for loop exercise with an accumulator variable.
Control flowNot started
Related lessons
Frequently asked questions
Does a C# switch statement fall through like C?
No. If a case has any statements, it must end with break, return, throw or goto case, and the compiler reports an error otherwise. Only an empty case label may sit directly above another, which is how you give several values one body: case 2: case 3: ... break;.
What is the difference between while and do-while in C#?
while checks its condition before each pass, so the body may never run. do { } while (condition); runs the body first and checks afterwards, so the body runs at least once. Use do-while for retry loops where you need one attempt before you can judge the result.
Can I use break to exit two nested loops at once?
break only leaves the loop it is written in. To leave both, set a flag that the outer loop's condition checks, or put the loops in a method and return from it, which is usually the clearer option. C# also has goto, but it is almost never the right tool.
How this page was checked. Every program on it was run with .NET SDK 8.0 at build time by the publishing checks, and the output shown is what it printed. Running C# inside the browser is not available yet, so the Run button is absent rather than pretending; copy the code and run it with .NET SDK 8.0 locally.