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#!");

Let the compiler infer the variable's type.

 city = "Lisbon";

Declare a value that can never change.

 int DaysPerWeek = 7;

The suffix that makes a literal a decimal.

decimal price = 19.99;

Cast a double to an int (the fraction is dropped).

int whole = ()9.99;

Parse text into a number without throwing on bad input.

if (int.(text, out int n)) Console.WriteLine(n);

Use a fallback when the input is null.

string name = Console.ReadLine()  "";

Insert a value into an interpolated string.

Console.WriteLine("Total: {total}");

Control flow and strings

A switch expression arm that matches anything else.

string size = n switch { < 10 => "small",  => "large" };

Loop over every item of a collection.

 (string name in names) Console.WriteLine(name);

Skip the rest of the current loop pass.

if (n % 2 == 0) ;

Compare two strings ignoring case.

bool same = a.Equals(b, StringComparison.);

Remove spaces from both ends of a string.

string clean = input.();

Format a number with two decimal places.

Console.WriteLine($"{price:}");

Build a long string efficiently in a loop.

var sb = new ();

Methods and collections

A method that returns nothing.

static  PrintBanner(string title) { /* ... */ }

Give a parameter a default value.

static string Greet(string name, string greeting  "Hello") => $"{greeting}, {name}";

Accept any number of arguments.

static int Sum( int[] numbers) => numbers.Sum();

A parameter the method must assign before returning.

static bool TryDivide(int a, int b,  int result) { /* ... */ }

The number of elements in an array.

int count = scores.;

Add an item to a list.

tasks.("Write report");

Look up a dictionary key safely.

if (stock.("apples", out int qty)) Console.WriteLine(qty);

An index meaning "the last element".

Index last = ;

Object-oriented programming

Create an object.

var account =  BankAccount("Rosa");

A property that can be read anywhere but only set inside the class.

public decimal Balance { get;  set; }

A property that can only be set while the object is being created.

public string Name { get; ; }

A member shared by all instances of a class.

private  int s_count;

Call the base class constructor.

public Car(string brand) : (brand) { }

Let derived classes replace a method.

public  string Speak() => "...";

A class that can't be instantiated and may have members without bodies.

public  class Shape { public abstract double Area(); }

A data type with value equality and a readable ToString, in one line.

public  Point(int X, int Y);

Create a modified copy of a record.

var moved = point  { X = 10 };

Types, LINQ, errors and async

An int that may also be null.

int rating = null;

Test a type and get a typed variable in one step.

if (shape  Circle c) Console.WriteLine(c.Radius);

A generic method with a type parameter T.

static T First(List<T> items) => items[0];

Keep only the elements that match a condition.

var adults = people.(p => p.Age >= 18);

Transform each element.

var names = people.(p => p.Name);

Group elements by a key.

var byCity = customers.(c => c.City);

Produce the values of a sequence one at a time.

 return n;

Code that always runs, whether or not an exception was thrown.

try { Save(); } catch (IOException) { Log(); }  { Close(); }

Dispose a resource automatically at the end of the scope.

 var writer = new StreamWriter("log.txt");

Wait for an asynchronous operation to finish.

string html =  http.GetStringAsync(url);

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.

Try it yourself · Program.cs
Edit the code, copy it into Program.cs and run it with dotnet run Program.cs (.NET 10 or later). How to set up .NET
Show a model solution
C#
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}");
}
Output
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.

Try it yourself · Program.cs
Edit the code, copy it into Program.cs and run it with dotnet run Program.cs (.NET 10 or later). How to set up .NET
Show a model solution
C#
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);
    }
}
Output
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.

Try it yourself · Program.cs
Edit the code, copy it into Program.cs and run it with dotnet run Program.cs (.NET 10 or later). How to set up .NET
Show a model solution
C#
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);
}
Output
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.

Try it yourself · Program.cs
Edit the code, copy it into Program.cs and run it with dotnet run Program.cs (.NET 10 or later). How to set up .NET
Show a model solution
C#
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;
    }
}
Output
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.

Try it yourself · Program.cs
Edit the code, copy it into Program.cs and run it with dotnet run Program.cs (.NET 10 or later). How to set up .NET
Show a model solution
TXT grades.csv
Ana,82,91,77
Ben,65,abc,70
Cleo,95,88,99
Dev,54,61,58
C#
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)");
Output
Ana    83.3  B
Cleo   94.0  A
Dev    57.7  F
Best: Cleo; skipped 1 line(s)