| Property | Value |
|---|---|
| Category | Composition (JustDummies.Composition) |
| Severity | 🟡 Warning |
| Enabled by default | Yes |
Distinctness is declared over an element type that has no value equality. The default comparer falls back to reference equality, and every element the generator builds is a new instance — so the requirement is satisfied by construction and constrains nothing.
The collection can hold the same value several times, which is precisely what the declaration asks it not to.
Noncompliant
public sealed class Box { // neither Equals nor IEquatable
public Box(int value) { Value = value; }
public int Value { get; }
}
Any.ListOf(Any.Int32().Between(1, 2).As(v => new Box(v))).Distinct().WithCount(6) // JD028
Measured on the library, that declaration returns six “distinct” boxes holding [1, 1, 1, 2, 1, 2] — green, every time.
Compliant
Give the element type value equality:
public sealed record Box(int Value);
Any.ListOf(Any.Int32().Between(1, 2).As(v => new Box(v))).Distinct().WithCount(6)
// AnyGenerationException: the element generator produced only 2 distinct value(s)
Now the declaration means something, and the impossible request is reported.
Or answer the equality question explicitly:
Any.ListOf(generator).Distinct(new BoxByValue())
Any.SetOf(generator, new BoxByValue())
Why the library cannot report this
From its side the requirement is met: the draws really are pairwise unequal under the comparer it was given, and there is nothing to complain about. Only the element type’s equality tells the inert case from the real one, and that is visible at the call site.
What it does not flag
- A generator that hands back existing instances.
Any.SetOf(Any.OneOf(first, second))returns the very references it was given, so drawing the same member twice yields the same reference and distinctness binds exactly as asked. - An element type that is a value type, a record, an
IEquatable<T>implementer, or that overridesEquals— anywhere in its base chain. - A non-sealed element type. A derived instance is free to add the equality the base lacks, and the rule only claims what it can prove.
- A projection that may return a shared instance (
As(v => Lookup(v))); only a chain that provably builds a new value here qualifies. - Any collection given an explicit comparer.