collections-and-equalitylisted
Install: claude install-skill Sarmkadan/dotnet-senior-skills
# Collections and Equality
## Return types: promise the least
- Public API returns: `IReadOnlyList<T>` / `IReadOnlyCollection<T>` for materialized data. Returning `List<T>` invites callers to mutate your internal state; returning `IEnumerable<T>` from a method that already has a list hides `Count` and invites re-enumeration paranoia (`.ToList()` calls sprinkled by nervous callers).
- Return `IEnumerable<T>` only when the sequence is genuinely lazy/streaming - and then the method name or docs say so, because every enumeration re-executes (the multiple-enumeration bug is in the performance skill; here the point is: do not create the ambiguity).
- Never return `null` for an empty collection: `Array.Empty<T>()` / `[]`. Every caller null-check on a collection return is a design apology.
- Parameters: accept the weakest thing you actually need - `IEnumerable<T>` if you only iterate once, `IReadOnlyCollection<T>` if you need `Count`. A parameter typed `List<T>` forces callers to copy.
## Exposed mutable collections
```csharp
// WRONG: any consumer can do order.Lines.Clear() - the invariant has a side door
public List<OrderLine> Lines { get; set; } = new();
// RIGHT: mutation goes through the method that enforces the rules
private readonly List<OrderLine> _lines = new();
public IReadOnlyCollection<OrderLine> Lines => _lines.AsReadOnly();
public void AddLine(OrderLine line) { /* rules */ _lines.Add(line); }
```
Note `AsReadOnly()` wraps (view of live list, cheap); `ToList()` in a