2 · Model and validate · 25 MIN
Records, nullable values and validation
A value model should make invalid states difficult to create.
A record supplies value-oriented equality, but its constructor still needs rules appropriate to the domain. This record validates a category and a nonnegative amount before exposing get-only properties. Nullable reference annotations help the compiler highlight potential null use; they do not prevent invalid values arriving from JSON or a database. Runtime validation remains necessary at external boundaries. Records are shallowly immutable, so mutable child collections need defensive copies when ownership should be isolated.
Enforce invariants when a domain value is constructed.
Read the example
using System;
var first = new Expense("food", 5);
Console.WriteLine(first == new Expense("food",5));
Console.WriteLine(first.Category);
public sealed record Expense {
public string Category { get; }
public int Amount { get; }
public Expense(string category, int amount) {
if (string.IsNullOrWhiteSpace(category) || amount < 0) throw new ArgumentException("Invalid expense");
Category = category; Amount = amount;
}
}Check the expected output
True food
Your challenge
Add an immutable report record and validate its category list by copying incoming values.
Solution cost: O(1) scalar construction; defensive copying costs O(n). time · Account for collection storage separately from the returned result. space
Common trap
Nullable annotations document intent but do not validate deserialized data.
Study the project implementation
using System;
var first = new Expense("food", 5);
Console.WriteLine(first == new Expense("food",5));
Console.WriteLine(first.Category);
public sealed record Expense {
public string Category { get; }
public int Amount { get; }
public Expense(string category, int amount) {
if (string.IsNullOrWhiteSpace(category) || amount < 0) throw new ArgumentException("Invalid expense");
Category = category; Amount = amount;
}
}Further reading: Official documentation
Next lesson: TryParse and expected failures →