C# flow control
last modified October 18, 2026
In this article we talk about the flow control in C#. We define several keywords that enable us to control the flow of a C# program.
When a C# program is run, the code is executed from top to bottom. The flow of the
program can be altered with various keywords, including if/else,
switch, for, while, break, and
continue.
The control flow structures can be used to execute code conditionally or multiple times. The programs in this tutorial use top-level statements. Where a random number is needed, the generator has a fixed seed, so that the output is the same on every run.
C# if statement
The if statement has the following general form:
if (expression)
{
statement;
}
The if keyword is used to check if an expression is true. If it is true,
a statement is then executed. The statement can be a single statement or a compound
statement. A compound statement consists of multiple statements enclosed in a
block. A block is code enclosed in curly brackets.
int n = new Random(5).Next(-5, 5);
Console.WriteLine(n);
if (n > 0)
{
Console.WriteLine("The n variable is positive");
}
// without braces only the next statement belongs to the if
if (n > 0)
Console.WriteLine("positive");
Console.WriteLine("this line is always executed");
// conditions combine with &&, || and !
if (n > -3 && n < 3 && n != 0)
{
Console.WriteLine("small and not zero");
}
if (!(n > 0) || n == 4)
{
Console.WriteLine("not positive, or four");
}
A random number is generated. If the number is greater than zero, we print a message to the terminal.
int n = new Random(5).Next(-5, 5);
This line generates a random integer in the range -5 to 4. The upper bound of
Next is exclusive.
if (n > 0)
{
Console.WriteLine("The n variable is positive");
}
Using the if keyword, we check if the generated number is greater than
zero. The if keyword is followed by a pair of round brackets. Inside the
brackets, we place an expression. The expression must result in a boolean value; in
C#, an integer is not a condition (unlike in C or C++), so if (n) does
not compile. If the value is true, the block enclosed by two curly brackets is
executed. In our case, the number is -2, so nothing is printed.
if (n > 0)
Console.WriteLine("positive");
Console.WriteLine("this line is always executed");
The curly brackets are optional if there is only one statement. This is a source of bugs when a second line is added later with the indentation of the first one; it is safer to always use braces.
if (n > -3 && n < 3 && n != 0)
Conditions are combined with the logical operators && (and),
|| (or), and ! (not). The operators &&
and || are short-circuiting: the right operand is evaluated only if the
left one does not decide the result. This allows idioms like
s != null && s.Length > 0.
$ dotnet run -2 this line is always executed small and not zero not positive, or four
C# else statement
We can use the else keyword to create a simple branch. If the expression
following the if keyword evaluates to false, the statement following
the else keyword is automatically executed. With else if,
we create multiple branches: the condition is tested only if all the previous
conditions were not met.
foreach (int n in new[] { -3, 0, 2 })
{
if (n > 0)
{
Console.WriteLine($"{n}: The number is positive");
}
else
{
Console.WriteLine($"{n}: The number is not positive");
}
}
Console.WriteLine("--");
foreach (int n in new[] { -3, 0, 2 })
{
if (n < 0)
{
Console.WriteLine($"{n}: The n variable is negative");
}
else if (n == 0)
{
Console.WriteLine($"{n}: The n variable is zero");
}
else
{
Console.WriteLine($"{n}: The n variable is positive");
}
}
Console.WriteLine("--");
// the conditional (ternary) operator for a simple choice between two values
int x = 7;
string parity = x % 2 == 0 ? "even" : "odd";
Console.WriteLine($"{x} is {parity}");
// the null-coalescing operators
string? name = null;
Console.WriteLine(name ?? "anonymous");
name ??= "guest";
Console.WriteLine(name);
Console.WriteLine(name?.Length);
// an early return (guard clause) instead of nesting
Console.WriteLine(Describe(null));
Console.WriteLine(Describe(""));
Console.WriteLine(Describe("falcon"));
static string Describe(string? s)
{
if (s is null)
{
return "no value";
}
if (s.Length == 0)
{
return "empty";
}
return $"text of {s.Length} characters";
}
The first loop takes three numbers and uses if/else. In
that program, zero was given to the non-positive numbers, which the second part fixes
with else if.
if (n < 0)
{
...
}
else if (n == 0)
{
...
}
else
{
...
}
If the first condition is true, the first block is executed and the remaining two are
skipped. Otherwise the second condition is checked, and if that is also false, the
else block is executed. The else block is always executed
if the previous conditions were not met.
string parity = x % 2 == 0 ? "even" : "odd";
The conditional operator ?: is an expression and chooses one of two
values. It suits simple cases; a nested chain of conditional operators is hard to
read, and a switch expression is better.
Console.WriteLine(name ?? "anonymous"); name ??= "guest"; Console.WriteLine(name?.Length);
The null-coalescing operator ?? provides a fallback for a
null value, ??= assigns it only when the variable is
null, and the null-conditional operator ?. accesses a member
only if the object is not null.
if (s is null)
{
return "no value";
}
A guard clause leaves the method early for the special cases. The main path
of the method stays at one level of indentation and is easier to read than a pyramid
of nested if statements.
$ dotnet run -3: The number is not positive 0: The number is not positive 2: The number is positive -- -3: The n variable is negative 0: The n variable is zero 2: The n variable is positive -- 7 is odd anonymous guest 5 no value empty text of 6 characters
C# switch statement
The switch statement is a selection control flow statement. It allows
the value of a variable or an expression to control the flow of program execution
via a multi-way branch. It creates multiple branches in a simpler way than a
combination of if, else if, and else.
The switch keyword is used to test a value of a variable or an expression
against a list of options. The options are written with the case
keyword. If a value matches, the statements following the case are
executed. The optional default section is executed if no other match is
found. Each section must end with break or another statement that
leaves it (return, throw, continue,
goto); there is no implicit fall-through as in C.
Selecting the day of week
In the following example, we select a day of the week with the switch
statement.
var dayOfWeek = new DateTime(2026, 10, 18).DayOfWeek;
switch (dayOfWeek)
{
case DayOfWeek.Sunday:
Console.WriteLine("dies Solis");
break;
case DayOfWeek.Monday:
Console.WriteLine("dies Lunae");
break;
case DayOfWeek.Tuesday:
Console.WriteLine("dies Martis");
break;
case DayOfWeek.Wednesday:
Console.WriteLine("dies Mercurii");
break;
case DayOfWeek.Thursday:
Console.WriteLine("dies Jovis");
break;
case DayOfWeek.Friday:
Console.WriteLine("dies Veneris");
break;
case DayOfWeek.Saturday:
Console.WriteLine("dies Saturni");
break;
}
// several labels share a section; there is no implicit fall-through
switch (dayOfWeek)
{
case DayOfWeek.Saturday:
case DayOfWeek.Sunday:
Console.WriteLine("weekend");
break;
default:
Console.WriteLine("working day");
break;
}
// goto case continues in another section
int level = 1;
switch (level)
{
case 1:
Console.WriteLine("level 1");
goto case 2;
case 2:
Console.WriteLine("level 2");
break;
}
The example determines the day of the week of a fixed date and prints its Latin
equivalent. Then it shows labels that share a section, and goto case.
switch (dayOfWeek)
{
...
}
In the round brackets, the switch keyword takes the value that is going
to be tested. The body of the switch is placed inside a pair of curly
brackets. In the body, we place multiple case labels.
case DayOfWeek.Saturday:
case DayOfWeek.Sunday:
Console.WriteLine("weekend");
break;
Several labels can lead to the same statements. This is the only kind of
"fall-through" that C# allows. goto case 2; in the last switch jumps to
another section on purpose.
$ dotnet run dies Solis weekend level 1 level 2
October 18, 2026 is a Sunday.
Selecting a domain
The input values are tested against a list of options. A case label can
be a constant of a string, a number, a character, an enum, or null.
foreach (string input in new[] { " us", "DE", "sk ", "pl", null! })
{
string? domain = input?.Trim().ToLower();
switch (domain)
{
case "us":
Console.WriteLine("United States");
break;
case "de":
Console.WriteLine("Germany");
break;
case "sk":
Console.WriteLine("Slovakia");
break;
case "hu":
Console.WriteLine("Hungary");
break;
case null:
Console.WriteLine("No domain");
break;
default:
Console.WriteLine($"Unknown: {domain}");
break;
}
}
The program normalizes several domain names and tests the result against a list of options.
string? domain = input?.Trim().ToLower();
The Trim method strips the leading and trailing white space, and
ToLower converts the characters to lowercase. Now "us", "US", and " us"
are the same domain. The null-conditional operator keeps a null input
as null.
case null:
Console.WriteLine("No domain");
break;
A null value can be matched by its own case. Without it, the
switch on a null string goes to the default section.
default:
Console.WriteLine($"Unknown: {domain}");
break;
The default keyword is optional. If none of the case
options matches, then the default section is executed.
$ dotnet run United States Germany Slovakia Unknown: pl No domain
Patterns and the when clause
Since C# 7, the labels of a switch can be patterns: type
patterns, relational patterns (<, >=), logical
patterns (and, or, not), and property patterns.
A when clause adds an extra condition to a label.
foreach (var age in new[] { -4, 12, 35, 150 })
{
switch (age)
{
case < 0:
Console.WriteLine("Age cannot be a negative value");
break;
case > 130:
Console.WriteLine("This is an unlikely high age");
break;
case >= 18 and < 65:
Console.WriteLine($"{age}: an adult");
break;
case var a when a < 18:
Console.WriteLine($"{age}: a minor");
break;
default:
Console.WriteLine($"The entered age is {age}");
break;
}
}
// type patterns
foreach (object o in new object[] { 42, "text", 3.14, null!, new int[] { 1, 2 }, DateTime.MinValue })
{
switch (o)
{
case int i when i > 40:
Console.WriteLine($"a big int {i}");
break;
case int i:
Console.WriteLine($"an int {i}");
break;
case string { Length: > 3 } s:
Console.WriteLine($"a long string {s}");
break;
case double d:
Console.WriteLine($"a double {d}");
break;
case int[] { Length: 2 } arr:
Console.WriteLine($"an array with two elements, first {arr[0]}");
break;
case null:
Console.WriteLine("null");
break;
default:
Console.WriteLine($"something else: {o.GetType().Name}");
break;
}
}
The example classifies ages with relational patterns and objects with type and property patterns.
case >= 18 and < 65:
Console.WriteLine($"{age}: an adult");
break;
case var a when a < 18:
Console.WriteLine($"{age}: a minor");
break;
The relational pattern compares with a constant, and and joins two
patterns. The var pattern puts the value in a variable, which can then
be used in the when clause. The sections are tested from top to bottom,
and the first match wins; the compiler reports a section that can never be reached
because of a more general one before it.
case string { Length: > 3 } s:
Console.WriteLine($"a long string {s}");
break;
A property pattern tests the properties of an object. Here, the object must be a
string with a length greater than three, and the variable s holds it.
$ dotnet run Age cannot be a negative value 12: a minor 35: an adult This is an unlikely high age a big int 42 a long string text a double 3.14 null an array with two elements, first 1 something else: DateTime
Using an enumeration as the match expression
For the matching expression, we can use any type. In the following example, we use an enumeration.
var color = (Color)new Random(3).Next(0, 7);
switch (color)
{
case Color.Red:
Console.WriteLine("The color is red");
break;
case Color.Green:
Console.WriteLine("The color is green");
break;
case Color.Blue:
Console.WriteLine("The color is blue");
break;
case Color.Brown:
Console.WriteLine("The color is brown");
break;
case Color.Yellow:
Console.WriteLine("The color is yellow");
break;
case Color.Pink:
Console.WriteLine("The color is pink");
break;
case Color.Orange:
Console.WriteLine("The color is orange");
break;
default:
Console.WriteLine("The color is unknown.");
break;
}
Console.WriteLine(color);
Console.WriteLine((int)color);
Console.WriteLine(Enum.IsDefined((Color)42));
// an enum can hold values that have no name, so keep the default section
Console.WriteLine((Color)42);
enum Color { Red, Green, Blue, Brown, Yellow, Pink, Orange }
The example generates a random value of the Color enumeration. The
switch statement determines which color was generated.
$ dotnet run The color is blue Blue 2 False 42
An enumeration variable can hold any value of its underlying type, not just the named
ones; (Color)42 is valid. This is the reason to keep a
default section, even if all the names are covered. Enum.IsDefined
tests whether a value is a member.
Switch expressions
Switch expressions make the syntax of a switch more concise. They were
introduced in C# 8.0. The variable comes before the switch keyword. The
case and : elements are replaced with =>,
and the default case with the _ discard. The arms are
expressions, not statements, and the result is a value.
foreach (var input in new[] { " us", "DE", "sk ", "pl" })
{
string result = input.Trim().ToLower() switch
{
"us" => "United States",
"de" => "Germany",
"sk" => "Slovakia",
"hu" => "Hungary",
_ => "Unknown"
};
Console.WriteLine(result);
}
Console.WriteLine("--");
// relational, logical and property patterns
Console.WriteLine(Grade(95) + Grade(72) + Grade(40));
static string Grade(int points) => points switch
{
>= 90 => "A",
>= 70 and < 90 => "B",
>= 50 and < 70 => "C",
_ => "F"
};
// tuple patterns combine several values
Console.WriteLine(Rps("rock", "scissors"));
Console.WriteLine(Rps("paper", "paper"));
Console.WriteLine(Rps("rock", "paper"));
static string Rps(string a, string b) => (a, b) switch
{
("rock", "scissors") or ("paper", "rock") or ("scissors", "paper") => "first wins",
("scissors", "rock") or ("rock", "paper") or ("paper", "scissors") => "second wins",
_ when a == b => "draw",
_ => "invalid"
};
// property patterns
var order = new Order("John", 120m, true);
Console.WriteLine(Discount(order));
Console.WriteLine(Discount(order with { Total = 40m }));
Console.WriteLine(Discount(order with { Premium = false }));
static decimal Discount(Order o) => o switch
{
{ Premium: true, Total: > 100 } => 0.15m,
{ Premium: true } => 0.05m,
{ Total: > 100 } => 0.02m,
_ => 0m
};
// a switch expression must be exhaustive
try
{
Console.WriteLine(Name(99));
}
catch (System.Runtime.CompilerServices.SwitchExpressionException e)
{
Console.WriteLine("SwitchExpressionException: " + e.Message);
}
static string Name(int n) => n switch { 1 => "one", 2 => "two" };
record Order(string Customer, decimal Total, bool Premium);
The example uses a switch expression to map the domains, grades, a tuple of two values, and the properties of an object.
static string Grade(int points) => points switch
{
>= 90 => "A",
>= 70 and < 90 => "B",
>= 50 and < 70 => "C",
_ => "F"
};
Relational and logical patterns express ranges directly. The arms are tested from top
to bottom, so the second arm could be simplified to >= 70, since 90
and more were already handled.
("rock", "scissors") or ("paper", "rock") or ("scissors", "paper") => "first wins",
A tuple pattern switches over several values at once, and or combines
alternatives.
{ Premium: true, Total: > 100 } => 0.15m,
A property pattern tests properties of the object, and replaces a series of
if statements. The first matching arm wins, so the most specific
patterns go first.
static string Name(int n) => n switch { 1 => "one", 2 => "two" };
A switch expression must be exhaustive: for each possible input there must
be an arm. The compiler warns (CS8509) about this example, since integers other than
1 and 2 are not covered. At run time, an unmatched value throws a
SwitchExpressionException. For enums, the warning appears even when all
the names are covered, because of values without a name. Add the _ arm
or throw an exception on purpose.
$ dotnet run United States Germany Slovakia Unknown -- ABF first wins draw second wins 0.15 0.05 0.02 SwitchExpressionException: Non-exhaustive switch expression failed to match its input. Unmatched value was 99.
C# while statement
The while statement is a control flow statement that allows code to be
executed repeatedly based on a given boolean condition.
while (expression)
{
statement;
}
The while keyword executes the statements inside the block enclosed by
the curly brackets. The statements are executed each time the expression evaluates to
true. The condition is tested before each cycle, so the body may never run.
int i = 0;
int sum = 0;
while (i < 10)
{
i++;
sum += i;
}
Console.WriteLine(sum);
// the condition is tested before every cycle: the body may never run
int k = 5;
while (k < 3)
{
Console.WriteLine("never printed");
}
// do-while runs at least once
int count = 0;
do
{
Console.WriteLine($"count is {count}");
count++;
} while (count < 0);
// a loop that reads until a sentinel
var queue = new Queue<int>([4, 8, 15, 16, 0, 23, 42]);
int total = 0;
while (queue.TryDequeue(out int value) && value != 0)
{
total += value;
}
Console.WriteLine($"total before the zero: {total}");
In the code example, we calculate the sum of the numbers 1 to 10, show a loop that
never runs, a do/while loop, and a loop that ends at a marker.
A loop has three parts: initialization, testing, and updating. Each execution of the body is called a cycle.
int i = 0;
while (i < 10)
{
i++;
sum += i;
}
We initialize the i variable, which is used as a counter. The expression
in the round brackets is the second phase, the testing. The statements in the body
are executed until the expression evaluates to false. Incrementing the counter is the
last phase, the updating. Note that improper handling of the while loops
may lead to endless cycles.
do
{
Console.WriteLine($"count is {count}");
count++;
} while (count < 0);
The do/while statement runs the body at least once, even if the condition
is not met, because the condition is tested after the body.
while (queue.TryDequeue(out int value) && value != 0)
A loop can read values until a marker: this loop takes items from a queue until the
queue is empty or a zero is found. The side-effect in the condition is a common
idiom with the TryXxx methods.
$ dotnet run 55 count is 0 total before the zero: 43
C# for statement
When the number of cycles is known before the loop starts, we can use the
for statement. In this construct we declare a counter variable which is
changed in each repetition of the loop. A for loop has three
parts in the header: the initialization, the condition, and the iteration.
for (int i = 0; i < 10; i++)
{
Console.Write($"{i} ");
}
Console.WriteLine();
string[] planets = ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"];
for (int i = 0; i < planets.Length; i++)
{
Console.WriteLine($"{i + 1}. {planets[i]}");
}
Console.WriteLine("In reverse:");
for (int i = planets.Length - 1; i >= 0; i--)
{
Console.Write($"{planets[i]} ");
}
Console.WriteLine();
// a step other than one
for (int i = 0; i <= 20; i += 5)
{
Console.Write($"{i} ");
}
Console.WriteLine();
// every part of the header is optional
int n = 0;
for (; ; )
{
if (++n == 3) break;
}
Console.WriteLine(n);
In this example, we print the numbers 0 to 9, traverse an array in both directions, use a different step, and write a loop with an empty header.
for (int i = 0; i < 10; i++)
{
Console.Write($"{i} ");
}
There are three phases. In the first phase, we initialize the counter i to
zero; this is done only once. Next comes the condition. If the condition is met, the
statements in the block are executed. In the third phase, the counter is increased.
Then we repeat phases 2 and 3 until the condition is not met and the loop is left.
for (int i = 0; i < planets.Length; i++)
{
Console.WriteLine($"{i + 1}. {planets[i]}");
}
A for loop is used for the traversal of an array. Arrays are accessed
by zero-based indexes, so the counter starts from zero and the condition checks if
the counter is less than the Length of the array. The loop
that counts down starts from Length - 1, the index of the last element,
and ends at zero.
for (; ; )
{
if (++n == 3) break;
}
All three parts of the header are optional. Without a condition, the loop runs until
a break.
$ dotnet run 0 1 2 3 4 5 6 7 8 9 1. Mercury 2. Venus 3. Earth 4. Mars 5. Jupiter 6. Saturn 7. Uranus 8. Neptune In reverse: Neptune Uranus Saturn Jupiter Mars Earth Venus Mercury 0 5 10 15 20 3
More expressions in the for loop
More expressions can be placed in the initialization and iteration part of the
for loop, separated by commas.
var r = new Random(1);
var values = new int[10];
int sum = 0;
int num = 0;
for (int i = 0; i < 10; i++, sum += num)
{
num = r.Next(10);
values[i] = num;
}
Console.WriteLine(string.Join(",", values));
Console.WriteLine("The sum of the values is {0}", sum);
Console.WriteLine(values.Sum() == sum);
// two variables in one loop
for (int lo = 0, hi = 9; lo < hi; lo++, hi--)
{
(values[lo], values[hi]) = (values[hi], values[lo]);
}
Console.WriteLine(string.Join(",", values));
// the variable of a for loop is captured by a lambda; each iteration has its own variable in foreach
var actions = new List<Action>();
for (int i = 0; i < 3; i++)
{
actions.Add(() => Console.Write($"{i} "));
}
actions.ForEach(a => a());
Console.WriteLine();
var actions2 = new List<Action>();
foreach (int i in Enumerable.Range(0, 3))
{
actions2.Add(() => Console.Write($"{i} "));
}
actions2.ForEach(a => a());
Console.WriteLine();
We create an array of ten pseudo-random numbers and calculate the sum of the numbers. Then we reverse the array with two counters, and look at how closures capture loop variables.
for (int i = 0; i < 10; i++, sum += num)
In the third part of the loop, we have two expressions separated by a comma. The
counter is incremented and the current number is added to the sum
variable. This is clever but obscure; in real code, the sum would be updated in the
body.
for (int lo = 0, hi = 9; lo < hi; lo++, hi--)
{
(values[lo], values[hi]) = (values[hi], values[lo]);
}
Two counters move from both ends toward the middle, and a tuple assignment swaps the elements.
for (int i = 0; i < 3; i++)
{
actions.Add(() => Console.Write($"{i} "));
}
A famous trap: the variable of a for loop is one variable shared by all
cycles. The lambdas capture the variable, not its value at the time, so all three
print 3 when called after the loop. In a foreach loop, each cycle has its
own variable, and the result is 0 1 2.
$ dotnet run 2,1,4,7,6,4,3,9,1,6 The sum of the values is 43 True 6,1,9,3,4,6,7,4,1,2 3 3 3 0 1 2
Nested for loops
Loops can be nested: a loop can be placed inside another loop. All the cycles of the nested loop are executed for each cycle of the outer loop.
string[] a1 = ["A", "B", "C"];
string[] a2 = ["A", "B", "C"];
for (int i = 0; i < a1.Length; i++)
{
for (int j = 0; j < a2.Length; j++)
{
Console.Write(a1[i] + a2[j] + " ");
}
Console.WriteLine();
}
// a multiplication table
for (int i = 1; i <= 4; i++)
{
for (int j = 1; j <= 5; j++)
{
Console.Write($"{i * j,4}");
}
Console.WriteLine();
}
// a triangle
for (int i = 1; i <= 4; i++)
{
Console.WriteLine(new string('*', i));
}
In this example, we create a Cartesian product of two arrays, a multiplication table, and a triangle.
for (int i = 0; i < a1.Length; i++)
{
for (int j = 0; j < a2.Length; j++)
{
Console.Write(a1[i] + a2[j] + " ");
}
Console.WriteLine();
}
A Cartesian product pairs each element of one array with all elements of the other. The inner loop is executed fully for each of the cycles of the outer loop. The cost of nested loops multiplies: two loops over n elements do n×n cycles.
$ dotnet run AA AB AC BA BB BC CA CB CC 1 2 3 4 5 2 4 6 8 10 3 6 9 12 15 4 8 12 16 20 * ** *** ****
C# foreach statement
The foreach construct simplifies traversing collections of data. It has
no explicit counter. The statement goes through the collection one element at a time,
and the current element is copied to a variable defined in the construct. See the
foreach tutorial for details.
string[] planets = ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"];
foreach (string planet in planets)
{
Console.Write($"{planet} ");
}
Console.WriteLine();
// the same with a list and a dictionary
var moons = new Dictionary<string, int> { ["Earth"] = 1, ["Mars"] = 2, ["Jupiter"] = 95 };
foreach (var (planet, count) in moons)
{
Console.WriteLine($"{planet} has {count} moon(s)");
}
In this example, we use the foreach statement to go through an array of
planets and a dictionary.
foreach (string planet in planets)
{
Console.Write($"{planet} ");
}
The planets is the array that we iterate through. The
planet is a temporary, read-only variable that has the current value.
$ dotnet run Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune Earth has 1 moon(s) Mars has 2 moon(s) Jupiter has 95 moon(s)
C# break statement
The break statement terminates the innermost loop (while,
do, for, foreach) or switch that
contains it.
C# continue statement
The continue statement skips the rest of the current cycle of a loop and
continues with the next iteration.
var random = new Random(7);
while (true)
{
int num = random.Next(1, 30);
Console.Write("{0} ", num);
if (num == 22)
{
break;
}
}
Console.WriteLine();
// continue skips the rest of the cycle
int n = 0;
while (n < 20)
{
n++;
if (n % 2 == 0)
{
continue;
}
Console.Write($"{n} ");
}
Console.WriteLine();
// break leaves only the innermost loop; to leave both we use a flag, a method with return, or goto
bool found = false;
for (int i = 1; i < 10 && !found; i++)
{
for (int j = 1; j < 10; j++)
{
if (i * j == 42)
{
Console.WriteLine($"{i} x {j} = 42");
found = true;
break;
}
}
}
Console.WriteLine(FindProduct(42));
static string FindProduct(int target)
{
for (int i = 1; i < 10; i++)
{
for (int j = 1; j < 10; j++)
{
if (i * j == target)
{
return $"{i} x {j} = {target}";
}
}
}
return "not found";
}
for (int i = 1; i < 10; i++)
{
for (int j = 1; j < 10; j++)
{
if (i * j == 56)
{
Console.WriteLine($"goto: {i} x {j}");
goto done;
}
}
}
done:
Console.WriteLine("finished");
The example draws random numbers until it gets 22, prints the odd numbers below 20, and then searches a product with nested loops in three ways.
while (true)
{
int num = random.Next(1, 30);
Console.Write("{0} ", num);
if (num == 22)
{
break;
}
}
We define an endless while loop and use the break statement
to get out of it. We choose a random value from 1 to 29, print it, and when it is 22,
we finish the loop.
if (n % 2 == 0)
{
continue;
}
If the expression n % 2 is 0, the number is even. The
continue statement skips the rest of the cycle, so the number is not
printed, and the next cycle starts.
bool found = false; for (int i = 1; i < 10 && !found; i++)
In nested loops, break leaves only the inner loop. To leave both, we can
use a flag in the condition of the outer loop, move the loops into a method and use
return, which is usually the cleanest, or use goto to a label
after the loops. goto is rarely used, but leaving deeply nested loops is
one of the situations where it is accepted.
$ dotnet run 12 26 20 2 11 20 2 28 25 25 14 28 13 4 26 12 24 10 26 1 22 1 3 5 7 9 11 13 15 17 19 6 x 7 = 42 6 x 7 = 42 goto: 7 x 8 finished
Exceptions and return
Exceptions and return change the flow too. The try
statement runs code that may fail; catch blocks handle the errors, and
the finally block runs always. return ends the method, and
throw ends it with an error.
// exceptions are a form of flow control, too
foreach (var text in new[] { "42", "x", null })
{
try
{
int n = int.Parse(text!);
Console.WriteLine($"parsed {n}");
}
catch (FormatException)
{
Console.WriteLine($"'{text}' is not a number");
}
catch (ArgumentNullException)
{
Console.WriteLine("null given");
}
finally
{
Console.WriteLine("-- done");
}
}
// when an error is expected, test instead of catching: it is faster and clearer
foreach (var text in new[] { "42", "x" })
{
Console.WriteLine(int.TryParse(text, out int n) ? $"parsed {n}" : $"'{text}' is not a number");
}
// return ends the current method, throw leaves it with an exception
Console.WriteLine(SafeDivide(10, 2));
Console.WriteLine(SafeDivide(1, 0));
static string SafeDivide(int a, int b)
{
if (b == 0)
{
return "cannot divide by zero";
}
return (a / b).ToString();
}
The program parses three inputs and handles each failure separately.
foreach (var text in new[] { "42", "x" })
{
Console.WriteLine(int.TryParse(text, out int n) ? $"parsed {n}" : $"'{text}' is not a number");
}
Exceptions are for exceptional situations. When an error is expected, as with user
input, test with TryParse instead of catching an exception; it is clearer
and much faster, since throwing an exception is expensive.
$ dotnet run parsed 42 -- done 'x' is not a number -- done null given -- done parsed 42 'x' is not a number 5 cannot divide by zero
Source
Selection statements and iteration statements
In this article we have covered C# control flow structures.
Author
List all C# tutorials.