// Exercise 3: hash-key correctness + variance (hard) // // Part A — Point is used as a Dictionary key. // The provided MutablePoint class overrides neither Equals nor GetHashCode, // so two points with the same coordinates are different keys. // 1. Implement the `GridPoint` readonly struct below with X/Y and correct // value equality: implement IEquatable, override Equals(object) // and GetHashCode (use HashCode.Combine), and add == / != operators. // (Alternatively make it a `readonly record struct` — but then explain // in a comment what the compiler generated for you.) // 2. Implement CountDistinct(points) using a HashSet. // // Part B — variance. // 3. Implement PrintAll(IEnumerable items) returning the items // joined with ",". In Main, the check passes a List to it — // explain in a comment WHY that compiles (IEnumerable), and why // passing a List where a List is expected would not. // // Part C — the mutable key trap. // 4. Read DemonstrateMutableKeyTrap. Predict what it prints BEFORE running // it, then write a comment explaining the result in terms of buckets. using System; using System.Collections.Generic; class MutablePoint { public int X; public int Y; public override int GetHashCode() => HashCode.Combine(X, Y); public override bool Equals(object? obj) => obj is MutablePoint p && p.X == X && p.Y == Y; } readonly struct GridPoint /* TODO: : IEquatable */ { public int X { get; } public int Y { get; } public GridPoint(int x, int y) { X = x; Y = y; } // TODO: Equals(GridPoint), Equals(object?), GetHashCode, ==, != } class Program { #pragma warning disable IDE0060 static int CountDistinct(IEnumerable points) { // TODO return 0; } static string PrintAll(IEnumerable items) { // TODO return ""; } #pragma warning restore IDE0060 static void DemonstrateMutableKeyTrap() { var set = new HashSet(); var p = new MutablePoint { X = 1, Y = 1 }; set.Add(p); p.X = 99; // mutate AFTER insertion Console.WriteLine($"Contains(p) after mutation: {set.Contains(p)}"); Console.WriteLine($"Count: {set.Count}"); // TODO: explain the output here } static void Main() { int pass = 0, total = 0; var pts = new[] { new GridPoint(1, 2), new GridPoint(1, 2), new GridPoint(2, 1), new GridPoint(0, 0) }; total++; if (CountDistinct(pts) == 3) { Console.WriteLine("PASS: CountDistinct uses value equality"); pass++; } else Console.WriteLine("FAIL: CountDistinct, got " + CountDistinct(pts)); total++; var dict = new Dictionary { [new GridPoint(5, 5)] = "treasure" }; if (dict.TryGetValue(new GridPoint(5, 5), out var v) && v == "treasure") { Console.WriteLine("PASS: GridPoint works as a dictionary key"); pass++; } else Console.WriteLine("FAIL: equal GridPoints are not equal keys"); total++; List names = new() { "a", "b" }; if (PrintAll(names) == "a,b") { Console.WriteLine("PASS: covariant IEnumerable"); pass++; } else Console.WriteLine("FAIL: PrintAll"); DemonstrateMutableKeyTrap(); Console.WriteLine($"--- {pass}/{total} checks passed ---"); } }