On this page
Control flow and functions in C# 14
Control flow in C#
Control flow determines the order in which statements execute. C# offers the classic structures plus expressive modern updates.
if / else
The most basic structure for making decisions:
int temperature = 35;
if (temperature > 40)
{
Console.WriteLine("Extreme heat");
}
else if (temperature > 30)
{
Console.WriteLine("Strong heat"); // ← executes
}
else if (temperature > 20)
{
Console.WriteLine("Pleasant temperature");
}
else
{
Console.WriteLine("Cold");
}
// Single-line if (no braces) — only when obvious
if (temperature > 0) Console.WriteLine("Above zero");Conditional expression (ternary)
bool isAdult = age >= 18;
string message = isAdult ? "Welcome" : "Access denied";
// Nested ternary (use sparingly)
string grade = points >= 90 ? "A" : points >= 70 ? "B" : points >= 50 ? "C" : "F";switch statement
To compare a value against multiple options:
string dayName = "Monday";
switch (dayName)
{
case "Monday":
case "Tuesday":
case "Wednesday":
case "Thursday":
case "Friday":
Console.WriteLine("Weekday");
break;
case "Saturday":
case "Sunday":
Console.WriteLine("Weekend");
break;
default:
Console.WriteLine("Invalid day");
break;
}switch expressions (C# 8+, improved in C# 14)
The modern, more expressive version. Returns a value directly:
// Over enums — the compiler verifies exhaustiveness
enum Status { Active, Inactive, Suspended, Deleted }
string DescribeStatus(Status status) => status switch
{
Status.Active => "User can access",
Status.Inactive => "Account temporarily deactivated",
Status.Suspended => "Account suspended for violation",
Status.Deleted => "Account permanently deleted"
// No default: the compiler warns if an enum case is missing
};
// With property patterns
record Order(decimal Total, string Country, bool IsPremium);
string CalculateShipping(Order o) => o switch
{
{ Total: > 100, Country: "US" } => "Free shipping",
{ IsPremium: true } => "Free shipping (premium)",
{ Country: "US", Total: > 50 } => "Discounted shipping",
_ => "Standard shipping"
};Loops
for — when you know the number of iterations
// Multiplication table of 5
for (int i = 1; i <= 10; i++)
{
Console.WriteLine($"5 × {i} = {5 * i}");
}
// Reverse iteration
for (int i = 10; i >= 1; i--)
{
Console.Write($"{i} ");
}
Console.WriteLine();
// for with multiple variables
for (int i = 0, j = 10; i < j; i++, j--)
{
Console.WriteLine($"i={i}, j={j}");
}foreach — for collections
string[] languages = { "C#", "Go", "Rust", "Python", "TypeScript" };
foreach (string lang in languages)
{
Console.WriteLine($" → {lang}");
}
// foreach with index using LINQ
foreach (var (lang, idx) in languages.Select((l, i) => (l, i)))
{
Console.WriteLine($"{idx + 1}. {lang}");
}while — while a condition holds
int attempts = 0;
int maxTries = 3;
bool success = false;
while (attempts < maxTries && !success)
{
Console.Write("Enter the password: ");
string pwd = Console.ReadLine() ?? "";
attempts++;
if (pwd == "dotnet10")
{
success = true;
Console.WriteLine("Access granted!");
}
else
{
Console.WriteLine($"Wrong password. Attempts: {attempts}/{maxTries}");
}
}do-while — executes at least once
int number;
do
{
Console.Write("Enter a positive number: ");
} while (!int.TryParse(Console.ReadLine(), out number) || number <= 0);
Console.WriteLine($"Valid number: {number}");break, continue, and return
// break — exits the loop
for (int i = 0; i < 100; i++)
{
if (i == 5) break;
Console.Write($"{i} "); // 0 1 2 3 4
}
// continue — skips to the next iteration
for (int i = 0; i < 10; i++)
{
if (i % 2 == 0) continue; // skip even numbers
Console.Write($"{i} "); // 1 3 5 7 9
}Methods (functions)
In C#, functions are called methods and always belong to a class or struct. The Program.cs entry point uses top-level statements, but internally it is also a method.
// Method with return type
static int Add(int a, int b)
{
return a + b;
}
// Expression-bodied method (single expression)
static int Multiply(int a, int b) => a * b;
// void method (no return)
static void Greet(string name)
{
Console.WriteLine($"Hello, {name}!");
}
// Optional parameters (must come last)
static string Format(string text, bool uppercase = false, char separator = ' ')
{
string result = text.Trim();
if (uppercase) result = result.ToUpper();
return result.Replace(' ', separator);
}
Console.WriteLine(Format("hello world")); // "hello world"
Console.WriteLine(Format("hello world", true)); // "HELLO WORLD"
Console.WriteLine(Format("hello world", true, '-')); // "HELLO-WORLD"Named arguments
// Named arguments improve readability
string result = Format(
text: "hello world",
separator: '_',
uppercase: true
);
Console.WriteLine(result); // "HELLO_WORLD"Local functions
Local functions are methods defined inside another method. They are only visible within the parent method:
static string ProcessOrder(string[] items, decimal discount)
{
// Local function — only visible here
decimal CalculateTotal()
{
decimal sum = 0;
foreach (string item in items)
sum += GetPrice(item);
return sum * (1 - discount);
}
decimal GetPrice(string item) => item switch
{
"laptop" => 999.99m,
"mouse" => 29.99m,
"keyboard" => 79.99m,
_ => 0m
};
decimal total = CalculateTotal();
return $"Total: ${total:F2}";
}Practice
- FizzBuzz: Use a
forloop from 1 to 100. If the number is divisible by 3, print "Fizz"; by 5, "Buzz"; by both, "FizzBuzz"; otherwise the number. - Calculator with switch expression: Create a method
Calculate(double a, double b, string operation)that uses a switch expression for +, -, *, / and returns the result. - params: Implement a method
Average(params double[] values)that returns the average of the received values.
In the next lesson we will learn how to define classes and objects — the core of object-oriented programming in C#.
Prefer switch expressions over switch statements
Switch expressions are more concise, force you to cover all cases, and can be used anywhere a value is expected (in assignments, return statements, etc.). The compiler warns when cases are missing for an enum type.
Local functions vs lambdas
Use local functions when you need recursion, when the code is complex, or when you want a descriptive name. Use lambdas when the code is very short and used only once (for example, in LINQ).
ref and out in production
Avoid ref and out in public APIs — they complicate the method signature and the calling code. Use them in performance-critical situations or when interacting with existing APIs that require them (such as TryParse).
// Switch expression — C# 8+ refined in C# 14
string DayOfWeekName(int day) => day switch
{
1 => "Monday",
2 => "Tuesday",
3 => "Wednesday",
4 => "Thursday",
5 => "Friday",
6 => "Saturday",
7 => "Sunday",
_ => throw new ArgumentOutOfRangeException(nameof(day), "Invalid day")
};
// Switch expression with guards (when clauses)
string ClassifyAge(int age) => age switch
{
< 0 => throw new ArgumentException("Invalid age"),
< 13 => "Child",
>= 13 and < 18 => "Teenager",
>= 18 and < 65 => "Adult",
>= 65 => "Senior"
};
Console.WriteLine(DayOfWeekName(3)); // Wednesday
Console.WriteLine(ClassifyAge(25)); // Adult
Sign in to track your progress