iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Exercises

Six C# exercises: collections, LINQ, async, records, pattern matching, error handling.

C# — exercises

EXAMPLE
// ===== Exercise 1: word count =====
public static Dictionary<string, int> WordCount(string s) {
    return s.ToLower()
        .Split(' ', StringSplitOptions.RemoveEmptyEntries)
        .GroupBy(w => w)
        .ToDictionary(g => g.Key, g => g.Count());
}

Console.WriteLine(string.Join(", ", WordCount("the cat sat on the mat")));

// ===== Exercise 2: group anagrams =====
public static List<List<string>> GroupAnagrams(IEnumerable<string> words) {
    return words
        .GroupBy(w => new string(w.OrderBy(c => c).ToArray()))
        .Select(g => g.ToList())
        .ToList();
}

// ===== Exercise 3: parallel async =====
public static async Task<List<string>> FetchAllAsync(IEnumerable<string> urls) {
    using var http = new HttpClient();
    var tasks = urls.Select(u => http.GetStringAsync(u));
    return (await Task.WhenAll(tasks)).ToList();
}

// ===== Exercise 4: record with validation =====
public record Money {
    public long Cents { get; }
    public string Currency { get; }

    public Money(long cents, string currency = "AUD") {
        if (cents < 0) throw new ArgumentOutOfRangeException(nameof(cents));
        if (currency.Length != 3) throw new ArgumentException("3-letter code");
        Cents = cents;
        Currency = currency;
    }

    public static Money operator +(Money a, Money b) {
        if (a.Currency != b.Currency) throw new ArgumentException("currency mismatch");
        return new Money(a.Cents + b.Cents, a.Currency);
    }
}

// ===== Exercise 5: discriminated union (sealed records) =====
public abstract record Shape;
public record Circle(double Radius) : Shape;
public record Square(double Side) : Shape;
public record Triangle(double Base, double Height) : Shape;

public static double Area(Shape s) => s switch {
    Circle c => Math.PI * c.Radius * c.Radius,
    Square sq => sq.Side * sq.Side,
    Triangle t => 0.5 * t.Base * t.Height,
    _ => throw new ArgumentException(nameof(s)),
};

// ===== Exercise 6: cancellation-aware retry =====
public static async Task<T> RetryAsync<T>(
    Func<CancellationToken, Task<T>> action,
    int maxAttempts = 3,
    TimeSpan? delay = null,
    CancellationToken ct = default
) {
    delay ??= TimeSpan.FromMilliseconds(100);
    for (int attempt = 1; ; attempt++) {
        try {
            return await action(ct);
        } catch (Exception) when (attempt < maxAttempts && !ct.IsCancellationRequested) {
            await Task.Delay(delay.Value * (int)Math.Pow(2, attempt - 1), ct);
        }
    }
}

// Use:
var result = await RetryAsync(async ct => await FetchUserAsync(id, ct));

// ===== Patterns =====
// - LINQ for collection transforms
// - Task.WhenAll for parallel async
// - Pattern matching with switch expressions
// - Records + validation in constructor
// - Cancellation token at every async boundary

// ===== Pitfalls =====
// - .Result on Task (deadlock risk)
// - LINQ to objects in hot loops (consider for/foreach)
// - Records with mutable fields (defeats value equality)
// - Forgetting to propagate CancellationToken

Why it matters

Six C# exercises drill the daily reflexes: LINQ + GroupBy, async with WhenAll, record validation, sealed hierarchies + switch expressions, retry with exponential backoff + cancellation. Modern .NET reads clean when these patterns are reflex.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
// Fill in: public ____ string Name { get; set; }
Try it Yourself »

Discussion

Loading…