C# Fundamentals: From Your First Program to Async and JSON · C# Basics: Types, Collections, Classes and Pattern Matching
Control Flow, Methods and Tuples
Branch with if and switch expressions, loop with for, foreach and while, write methods with optional and named arguments, and return several values with tuples.
What you'll build
A grading helper: methods that classify scores with a switch expression, compute statistics and return several results as a tuple, with loops that process a list of students.
The idea
Branching: if/else, the ternary cond ? a : b, and switch. Prefer the switch expression, which returns a value and supports relational patterns (>= 90):
string grade = score switch { >= 90 => "A", >= 80 => "B", _ => "C" };
_ is the discard, matching anything else.
Loops: for (index), foreach (each item, the usual choice), while/do-while, with break and continue.
Methods:
- Parameters can be optional (
int decimals = 1) and passed by name (Round(x, decimals: 2)). paramstakes any number of arguments:Sum(params int[] numbers).- Expression-bodied methods:
static int Square(int n) => n * n;. - Local functions live inside another method.
- Tuples return several values without defining a type:
(double Min, double Max) Range(...). Deconstruct withvar (min, max) = Range(...).
Methods in top-level programs are written as local functions, or in a class below the statements.
The code
var students = new Dictionary<string, int[]>
{
["Ada"] = [92, 88, 95],
["Grace"] = [78, 85, 80],
["Linus"] = [55, 62, 70],
};
foreach (var (name, scores) in students.OrderBy(s => s.Key))
{
var (min, max, average) = Summarize(scores);
Console.WriteLine($"{name,-6} min {min} max {max} avg {average:F1} grade {GradeFor(average)}");
}
// A while loop with break and continue
var numbers = new[] { 3, 8, 15, 4, 23, 42, 7 };
var index = 0;
var firstBig = -1;
while (index < numbers.Length)
{
if (numbers[index] % 2 == 1) { index++; continue; } // skip odd numbers
if (numbers[index] > 30) { firstBig = numbers[index]; break; }
index++;
}
Console.WriteLine($"first even number above 30: {firstBig}");
Console.WriteLine(Describe(5));
Console.WriteLine(Describe(5, unit: "km"));
Console.WriteLine(Join("-", "a", "b", "c"));
// A switch expression with relational patterns
static string GradeFor(double score) => score switch
{
>= 90 => "A",
>= 80 => "B",
>= 70 => "C",
_ => "F",
};
// A tuple return, with named elements
static (int Min, int Max, double Average) Summarize(int[] scores) =>
(scores.Min(), scores.Max(), scores.Average());
// Optional parameter with a default; callers can name arguments
static string Describe(int value, string unit = "m") => $"{value} {unit}";
// params accepts any number of arguments
static string Join(string separator, params string[] parts) => string.Join(separator, parts);
Output
Ada min 88 max 95 avg 91.7 grade A Grace min 78 max 85 avg 81.0 grade B Linus min 55 max 70 avg 62.3 grade F first even number above 30: 42 5 m 5 km a-b-c
Run this on your computer — see “Get Started” for setup.
Common mistakes
- Using
switchstatements with fall-through habits. C# requiresbreakorreturnper case; switch expressions avoid the issue. - Modifying a collection while iterating it with
foreach. It throwsInvalidOperationException; collect changes first or iterate a copy. - Overusing optional parameters in public APIs. Defaults are baked into callers at compile time; overloads are safer for libraries.
- Returning big tuples. Beyond three or so values, a
recordwith named properties is clearer. - Off-by-one errors with
for. Preferforeach, orfor (var i = 0; i < items.Length; i++)with<not<=.
Key points
- Switch expressions return values and support relational and other patterns.
foreachis the default loop;breakandcontinuecontrol flow.- Methods support optional, named and
paramsarguments, and expression bodies. - Tuples return several values and deconstruct with
var (a, b) = ....
Further reading: C# fundamentals: Methods.
Practice questions
What is the advantage of a switch expression over a switch statement?
Answer
It is an expression that returns a value, is more compact, supports relational and type patterns, and the compiler warns when cases are not exhaustive, so you can't forget to handle one.
When is a tuple a poor choice for a return value?
Answer
When it has many elements or is used widely. A record with named properties documents the meaning, can be extended and is easier to use across a codebase than a long tuple.
Open in the app to mark lessons complete, take notes and track your progress.