Why
Immutability is a deliberate design property: value objects, DTOs, messages, and event types are often required to be immutable so they can be shared freely across threads and cached safely. Today this convention can only be approximated by combining several member-level checks (readonly fields, properties without setters), which is verbose and easy to get subtly wrong (e.g. forgetting init-only setters or non-public members).
A dedicated assertion makes the intent explicit and keeps the rule in one place:
await Expect.That(In.AssemblyContaining<MyEvent>()
.Types().WhichAreClasses().WithName("*Event").AsWildcard())
.AreImmutable();
Proposal
- Assertions:
ThatType.IsImmutable() / IsNotImmutable() and ThatTypes.AreImmutable() / AreNotImmutable()
- Filter:
Filtered.Types.WhichAreImmutable() / WhichAreNotImmutable()
A type counts as immutable when all instance fields are readonly and all instance properties have no setter (or only an init setter). The failure message should name the mutable members, so violations are immediately actionable.
Open question
Should init setters count as immutable? Suggestion: yes by default, with an option to be strict.
Why
Immutability is a deliberate design property: value objects, DTOs, messages, and event types are often required to be immutable so they can be shared freely across threads and cached safely. Today this convention can only be approximated by combining several member-level checks (readonly fields, properties without setters), which is verbose and easy to get subtly wrong (e.g. forgetting init-only setters or non-public members).
A dedicated assertion makes the intent explicit and keeps the rule in one place:
Proposal
ThatType.IsImmutable()/IsNotImmutable()andThatTypes.AreImmutable()/AreNotImmutable()Filtered.Types.WhichAreImmutable()/WhichAreNotImmutable()A type counts as immutable when all instance fields are
readonlyand all instance properties have no setter (or only aninitsetter). The failure message should name the mutable members, so violations are immediately actionable.Open question
Should
initsetters count as immutable? Suggestion: yes by default, with an option to be strict.