You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Collections received from deserialization, binding, and caller-supplied arrays frequently have invariants about their contents rather than only their own nullability or size. Callers currently express these checks with manual loops or LINQ, losing the intent and the library's standard exception, nullability, caller-expression, and fluent-return behavior. Add MustNotContainNull and MustNotContainNullOrWhiteSpace as named collection guards.
The additions must work across every package target, enumerate safely and no more than once, preserve collection shapes, remain Native AOT compatible, and participate in the customizable single-file source distribution.
Acceptance Criteria
MustNotContainNull and MustNotContainNullOrWhiteSpace are available with identical semantics on .NET Standard 2.0, .NET Standard 2.1, and .NET 10.
MustNotContainNull accepts nullable reference collections implementing IEnumerable, rejects any null item (including an empty nullable value type boxed through non-generic enumeration), and accepts empty collections and collections containing only non-null items.
MustNotContainNullOrWhiteSpace accepts nullable reference collections of strings and rejects null, empty, or whitespace-only items using the same Unicode whitespace semantics as string.IsNullOrWhiteSpace; empty collections and collections of non-blank strings are accepted.
Both guards preserve and return the original reference collection shape, and dedicated overloads preserve ImmutableArray<T> without boxing. A default immutable array is treated as having no items and therefore succeeds.
Each invocation obtains at most one enumerator, disposes it normally, and stops at the first invalid item. Failure-message construction does not enumerate the input again, and validation uses constant additional space.
A null reference collection passed to a default overload throws ArgumentNullException, while invalid null or blank content throws ExistingItemException. Default messages identify the guarded expression, the failure category, and the zero-based position of the first offending item without rendering the whole collection.
Every overload returns the successfully validated input, captures the guarded expression for default exceptions, accepts an optional custom message, and has a custom-exception-factory counterpart consistent with the existing API and the receiver shape.
Automated tests cover empty, valid, and failing inputs; arrays, List<T>, lazy/single-use enumerables, nullable reference and value-type items, and default/empty/non-empty immutable arrays; null, empty, ASCII-whitespace, and Unicode-whitespace strings; null receivers; default exceptions and messages; custom messages and factories; caller argument expressions; fluent return types; early termination; single enumeration; and enumerator disposal.
The source-export whitelist catalog and committed settings contain both assertion families, and focused source-export tests verify their guards, exception and throw-helper reachability, immutable-array overloads, and exception-factory trimming.
The committed .NET Standard 2.0 single-file distribution is regenerated and validates with the new portable API surface.
XML documentation and the assertion overview describe the supported receiver shapes, vacuous success for empty/default immutable collections, the guards' O(n), constant-additional-space, single-pass, short-circuiting enumeration behavior, and the boxing incurred when MustNotContainNull enumerates value-type items through non-generic IEnumerable.
The complete solution restores and builds without warnings in Release configuration, and all automated tests pass on the pinned SDK.
Technical Details
Use one Check.<Assertion>.cs file per family. The null-content guard can preserve any reference receiver through non-generic enumeration because it needs no item type; the string guard can likewise infer the receiver while fixing the item type in its constraint (illustrative signatures):
Add factory counterparts receiving TCollection?, with the established [ContractAnnotation] and nullable-flow attributes. Add dedicated ImmutableArray<T> / ImmutableArray<string?> overloads and factories so the value-type receiver is neither boxed nor shape-erased. Checking each item with is null covers reference types and Nullable<T> values through IEnumerable; this non-generic enumeration boxes non-null value-type items, so call out that cost in the public XML remarks rather than implying the guard is allocation-free for those collections. The string family should use the existing IsNullOrWhiteSpace predicate. Do not compose the string guard from multiple guards, because that would enumerate a lazy input more than once.
Implement both guards with direct foreach-style loops and short-circuit on the first failure. Do not pre-count or call LINQ Any or Count; a lazy or stateful enumerable must be observed only once. Keep a zero-based position while iterating and pass the discovered value/category and position to throw helpers; throw helpers must not append collection contents because doing so would re-enumerate a possibly one-shot input.
Reuse ExistingItemException for prohibited null and blank items, with focused throw helpers for the two message forms. Custom messages replace the generated message exactly as in existing guards. A null collection follows MustNotBeNull; custom factories are invoked for both a null receiver and invalid contents. A default immutable array is handled before enumeration and succeeds, consistent with MustNotContain; callers that also need initialization or non-emptiness can compose MustNotBeDefaultOrEmpty.
Add both names to AssertionWhitelist and settings.json, cover dependency reachability and factory removal in SourceFileMergerWhitelistTests, and regenerate Light.GuardClauses.SingleFile.cs for .NET Standard 2.0. Document the new families in the collections section of docs/assertion-overview.md, and put the enumeration behavior and complexity in each public method's XML remarks. Boolean predicate counterparts are out of scope: the requested value is the named throwing precondition, while Any already covers non-throwing inspection.
Rationale
Collections received from deserialization, binding, and caller-supplied arrays frequently have invariants about their contents rather than only their own nullability or size. Callers currently express these checks with manual loops or LINQ, losing the intent and the library's standard exception, nullability, caller-expression, and fluent-return behavior. Add
MustNotContainNullandMustNotContainNullOrWhiteSpaceas named collection guards.The additions must work across every package target, enumerate safely and no more than once, preserve collection shapes, remain Native AOT compatible, and participate in the customizable single-file source distribution.
Acceptance Criteria
MustNotContainNullandMustNotContainNullOrWhiteSpaceare available with identical semantics on .NET Standard 2.0, .NET Standard 2.1, and .NET 10.MustNotContainNullaccepts nullable reference collections implementingIEnumerable, rejects any null item (including an empty nullable value type boxed through non-generic enumeration), and accepts empty collections and collections containing only non-null items.MustNotContainNullOrWhiteSpaceaccepts nullable reference collections of strings and rejects null, empty, or whitespace-only items using the same Unicode whitespace semantics asstring.IsNullOrWhiteSpace; empty collections and collections of non-blank strings are accepted.ImmutableArray<T>without boxing. A default immutable array is treated as having no items and therefore succeeds.ArgumentNullException, while invalid null or blank content throwsExistingItemException. Default messages identify the guarded expression, the failure category, and the zero-based position of the first offending item without rendering the whole collection.List<T>, lazy/single-use enumerables, nullable reference and value-type items, and default/empty/non-empty immutable arrays; null, empty, ASCII-whitespace, and Unicode-whitespace strings; null receivers; default exceptions and messages; custom messages and factories; caller argument expressions; fluent return types; early termination; single enumeration; and enumerator disposal.MustNotContainNullenumerates value-type items through non-genericIEnumerable.Technical Details
Use one
Check.<Assertion>.csfile per family. The null-content guard can preserve any reference receiver through non-generic enumeration because it needs no item type; the string guard can likewise infer the receiver while fixing the item type in its constraint (illustrative signatures):Add factory counterparts receiving
TCollection?, with the established[ContractAnnotation]and nullable-flow attributes. Add dedicatedImmutableArray<T>/ImmutableArray<string?>overloads and factories so the value-type receiver is neither boxed nor shape-erased. Checking each item withis nullcovers reference types andNullable<T>values throughIEnumerable; this non-generic enumeration boxes non-null value-type items, so call out that cost in the public XML remarks rather than implying the guard is allocation-free for those collections. The string family should use the existingIsNullOrWhiteSpacepredicate. Do not compose the string guard from multiple guards, because that would enumerate a lazy input more than once.Implement both guards with direct
foreach-style loops and short-circuit on the first failure. Do not pre-count or call LINQAnyorCount; a lazy or stateful enumerable must be observed only once. Keep a zero-based position while iterating and pass the discovered value/category and position to throw helpers; throw helpers must not append collection contents because doing so would re-enumerate a possibly one-shot input.Reuse
ExistingItemExceptionfor prohibited null and blank items, with focused throw helpers for the two message forms. Custom messages replace the generated message exactly as in existing guards. A null collection followsMustNotBeNull; custom factories are invoked for both a null receiver and invalid contents. A default immutable array is handled before enumeration and succeeds, consistent withMustNotContain; callers that also need initialization or non-emptiness can composeMustNotBeDefaultOrEmpty.Add both names to
AssertionWhitelistandsettings.json, cover dependency reachability and factory removal inSourceFileMergerWhitelistTests, and regenerateLight.GuardClauses.SingleFile.csfor .NET Standard 2.0. Document the new families in the collections section ofdocs/assertion-overview.md, and put the enumeration behavior and complexity in each public method's XML remarks. Boolean predicate counterparts are out of scope: the requested value is the named throwing precondition, whileAnyalready covers non-throwing inspection.