C# Exercises
Practise what you've learned. Type the missing piece into each highlighted gap and press Check. Further down are programming challenges with editors and model solutions — copy them into a file and run them with dotnet run.
Fill in the blanks
Basics
Print a line of text to the console.
Console.("Hello, C#!");
(lesson 03)
Let the compiler infer the variable's type.
city = "Lisbon";
(lesson 06)
Declare a value that can never change.
int DaysPerWeek = 7;
(lesson 06)
The suffix that makes a literal a decimal.
decimal price = 19.99;
Without it, 19.99 is a double (lesson 07).
Cast a double to an int (the fraction is dropped).
int whole = ()9.99;
(lesson 09)
Parse text into a number without throwing on bad input.
if (int.(text, out int n)) Console.WriteLine(n);
(lesson 09)
Use a fallback when the input is null.
string name = Console.ReadLine() "";
The null-coalescing operator (lessons 10–11).
Insert a value into an interpolated string.
Console.WriteLine("Total: {total}");
(lesson 03)
Control flow and strings
A switch expression arm that matches anything else.
string size = n switch { < 10 => "small", => "large" };
The discard pattern (lesson 13).
Loop over every item of a collection.
(string name in names) Console.WriteLine(name);
(lesson 15)
Skip the rest of the current loop pass.
if (n % 2 == 0) ;
(lesson 14)
Compare two strings ignoring case.
bool same = a.Equals(b, StringComparison.);
(lesson 16)
Remove spaces from both ends of a string.
string clean = input.();
(lesson 17)
Format a number with two decimal places.
Console.WriteLine($"{price:}");
F2 is fixed-point; N2 adds thousands separators (lesson 18).
Build a long string efficiently in a loop.
var sb = new ();
From System.Text (lesson 19).
Methods and collections
A method that returns nothing.
static PrintBanner(string title) { /* ... */ }
(lesson 20)
Give a parameter a default value.
static string Greet(string name, string greeting "Hello") => $"{greeting}, {name}";
(lesson 21)
Accept any number of arguments.
static int Sum( int[] numbers) => numbers.Sum();
(lesson 21)
A parameter the method must assign before returning.
static bool TryDivide(int a, int b, int result) { /* ... */ }
(lesson 22)
The number of elements in an array.
int count = scores.;
Lists use Count instead (lessons 25 and 27).
Add an item to a list.
tasks.("Write report");
(lesson 27)
Look up a dictionary key safely.
if (stock.("apples", out int qty)) Console.WriteLine(qty);
(lesson 28)
An index meaning "the last element".
Index last = ;
values[last] is the last element (lesson 30).
Object-oriented programming
Create an object.
var account = BankAccount("Rosa");
(lesson 31)
A property that can be read anywhere but only set inside the class.
public decimal Balance { get; set; }
(lesson 33)
A property that can only be set while the object is being created.
public string Name { get; ; }
(lesson 33)
A member shared by all instances of a class.
private int s_count;
(lesson 34)
Call the base class constructor.
public Car(string brand) : (brand) { }
(lesson 36)
Let derived classes replace a method.
public string Speak() => "...";
Derived classes use override (lesson 37).
A class that can't be instantiated and may have members without bodies.
public class Shape { public abstract double Area(); }
(lesson 38)
A data type with value equality and a readable ToString, in one line.
public Point(int X, int Y);
(lesson 40)
Create a modified copy of a record.
var moved = point { X = 10 };
(lesson 40)
Types, LINQ, errors and async
An int that may also be null.
int rating = null;
(lesson 43)
Test a type and get a typed variable in one step.
if (shape Circle c) Console.WriteLine(c.Radius);
(lesson 44)
A generic method with a type parameter T.
static T First(List<T> items) => items[0];
(lesson 45)
Keep only the elements that match a condition.
var adults = people.(p => p.Age >= 18);
(lesson 49)
Transform each element.
var names = people.(p => p.Name);
(lesson 49)
Group elements by a key.
var byCity = customers.(c => c.City);
(lesson 50)
Produce the values of a sequence one at a time.
return n;
(lesson 51)
Code that always runs, whether or not an exception was thrown.
try { Save(); } catch (IOException) { Log(); } { Close(); }
(lesson 52)
Dispose a resource automatically at the end of the scope.
var writer = new StreamWriter("log.txt");
(lesson 53)
Wait for an asynchronous operation to finish.
string html = http.GetStringAsync(url);
(lesson 57)
Programming challenges
1. Word frequencies
Count how often each word appears in a sentence, ignoring case and punctuation, and print the words with their counts — most frequent first, alphabetical for ties.
Show a model solution
string text = "The cat and the hat. The bat and the cat!"; var words = text .ToLower() .Split(' ', StringSplitOptions.RemoveEmptyEntries) .Select(w => w.Trim('.', ',', '!', '?')); foreach (var (word, count) in words.CountBy(w => w).OrderByDescending(p => p.Value).ThenBy(p => p.Key)) { Console.WriteLine($"{word,-4} {count}"); }
the 4 and 2 cat 2 bat 1 hat 1
2. A bank account class
Write a BankAccount class with an owner, a balance that can't be set from outside, Deposit and Withdraw methods that reject invalid amounts, and a transaction history.
Show a model solution
var acct = new BankAccount("Ana"); acct.Deposit(100m); acct.Withdraw(30m); try { acct.Withdraw(500m); } catch (InvalidOperationException ex) { Console.WriteLine(ex.Message); } Console.WriteLine($"{acct.Owner}: {acct.Balance:C}"); Console.WriteLine(string.Join(", ", acct.History)); class BankAccount(string owner) { private readonly List<decimal> _history = []; public string Owner { get; } = owner; public decimal Balance { get; private set; } public IReadOnlyList<decimal> History => _history; public void Deposit(decimal amount) { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(amount); Balance += amount; _history.Add(amount); } public void Withdraw(decimal amount) { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(amount); if (amount > Balance) throw new InvalidOperationException($"Insufficient funds: balance is {Balance:C}."); Balance -= amount; _history.Add(-amount); } }
Insufficient funds: balance is $70.00. Ana: $70.00 100, -30
3. Shapes with an interface
Define an IShape interface with Area and Perimeter. Implement it with records for a circle, a rectangle and a right triangle, then print them sorted by area.
Show a model solution
List<IShape> shapes = [new Circle(1.5), new Rectangle(2, 5), new RightTriangle(3, 4)]; foreach (IShape s in shapes.OrderBy(s => s.Area())) { Console.WriteLine($"{s.GetType().Name,-14} area {s.Area(),6:F2} perimeter {s.Perimeter(),6:F2}"); } interface IShape { double Area(); double Perimeter(); } record Circle(double Radius) : IShape { public double Area() => Math.PI * Radius * Radius; public double Perimeter() => 2 * Math.PI * Radius; } record Rectangle(double Width, double Height) : IShape { public double Area() => Width * Height; public double Perimeter() => 2 * (Width + Height); } record RightTriangle(double A, double B) : IShape { public double Area() => A * B / 2; public double Perimeter() => A + B + Math.Sqrt(A * A + B * B); }
RightTriangle area 6.00 perimeter 12.00 Circle area 7.07 perimeter 9.42 Rectangle area 10.00 perimeter 14.00
4. A generic stack
Write a generic BoundedStack<T> with a maximum size. Push should throw when it's full, and TryPop should follow the Try pattern.
Show a model solution
var stack = new BoundedStack<string>(2); stack.Push("a"); stack.Push("b"); try { stack.Push("c"); } catch (InvalidOperationException ex) { Console.WriteLine(ex.Message); } while (stack.TryPop(out string? item)) { Console.WriteLine($"Popped {item}"); } Console.WriteLine($"Empty: {stack.Count == 0}"); class BoundedStack<T>(int capacity) { private readonly List<T> _items = []; public int Count => _items.Count; public void Push(T item) { if (_items.Count == capacity) throw new InvalidOperationException($"Stack is full ({capacity} items)."); _items.Add(item); } public bool TryPop(out T? item) { if (_items.Count == 0) { item = default; return false; } item = _items[^1]; _items.RemoveAt(_items.Count - 1); return true; } }
Stack is full (2 items). Popped b Popped a Empty: True
5. Grade report from a file
Read a CSV file of student scores, then print each student's average and grade, and the class's best student. Skip lines that can't be parsed and report how many were skipped.
Show a model solution
Ana,82,91,77 Ben,65,abc,70 Cleo,95,88,99 Dev,54,61,58
var students = new List<(string Name, double Average)>(); int skipped = 0; foreach (string line in File.ReadLines("grades.csv")) { string[] parts = line.Split(','); var scores = parts.Skip(1).Select(p => int.TryParse(p, out int s) ? s : (int?)null).ToList(); if (parts.Length < 2 || scores.Any(s => s is null)) { skipped++; continue; } students.Add((parts[0], scores.Average(s => s!.Value))); } foreach (var (name, avg) in students) { string grade = avg switch { >= 90 => "A", >= 80 => "B", >= 65 => "C", _ => "F" }; Console.WriteLine($"{name,-5}{avg,6:F1} {grade}"); } Console.WriteLine($"Best: {students.MaxBy(s => s.Average).Name}; skipped {skipped} line(s)");
Ana 83.3 B Cleo 94.0 A Dev 57.7 F Best: Cleo; skipped 1 line(s)