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
public static Type MustBeAssignableTo([NotNull][ValidatedNotNull] this Type? parameter, [NotNull][ValidatedNotNull] Type? requiredType, Func<Type?, Type?, Exception> exceptionFactory)
2134
+
{
2135
+
if (parameter is null || requiredType is null || !requiredType.IsAssignableFrom(parameter))
/// Ensures that the string is standard Base64 with valid padding. Space, tab, carriage return, and line feed are ignored.
2088
2145
/// Empty and whitespace-only strings are valid.
@@ -11800,6 +11857,13 @@ public static void InvalidMinimumImmutableArrayLength<T>(ImmutableArray<T> param
11800
11857
[DoesNotReturn]
11801
11858
public static void MustBeApproximately<T>(T parameter, T other, T tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be approximately equal to {other} with a tolerance of {tolerance}, but it actually is {parameter}.");
11802
11859
/// <summary>
11860
+
/// Throws the default <see cref = "ArgumentException"/> indicating that values of the candidate type cannot be
11861
+
/// assigned to variables of the required type, using the optional parameter name and message.
11862
+
/// </summary>
11863
+
[ContractAnnotation("=> halt")]
11864
+
[DoesNotReturn]
11865
+
public static void MustBeAssignableTo(Type parameter, Type requiredType, string? parameterName = null, string? message = null) => throw new ArgumentException(message ?? $"Values of type \"{parameter}\" must be assignable to variables of type \"{requiredType}\", but they are not.", parameterName);
11866
+
/// <summary>
11803
11867
/// Throws the default <see cref = "ArgumentOutOfRangeException"/> indicating that a comparable value must be greater
11804
11868
/// than the given boundary value, using the optional parameter name and message.
Parent issue #162 (point 3) identifies six BrilliantMessaging guards that reject a `Type` unless it can be assigned to a required base type or interface. The existing type-relation predicates do not provide a direct throwing assertion, so callers must translate the relationship into a boolean condition and choose the correct comparison direction themselves.
6
+
7
+
Add `MustBeAssignableTo` with the exact semantics of `Type.IsAssignableFrom`, expressed in the fluent direction `candidateType.MustBeAssignableTo(requiredType)`, and keep it available on `netstandard2.0`. Do not add `MustImplement`: the assignability guard already covers interfaces, while a second API would duplicate behavior and risk suggesting the open-generic equivalence semantics of the existing `Implements` predicate.
8
+
9
+
## Acceptance Criteria
10
+
11
+
-[x]`candidateType.MustBeAssignableTo(requiredType, parameterName, message)` returns the original candidate `Type` when `requiredType.IsAssignableFrom(candidateType)` is true and throws `ArgumentException` when it is false, identically on .NET Standard 2.0, .NET Standard 2.1, and .NET 10; the exception exposes the candidate parameter name and honors an optional custom message.
12
+
-[x] The default overload throws `ArgumentNullException` when either the candidate or required type is null, attributing a null candidate to the caller-captured parameter name and a null required type to `requiredType`; no new public exception type is introduced.
13
+
-[x] A custom-exception-factory overload accepts `Func<Type?, Type?, Exception>`, passes the original candidate and required types to the factory, invokes it only when either input is null or the assignability check fails, and a null factory on a failing check throws `ArgumentNullException` via the existing `Throw.CustomException` convention.
14
+
-[x] Automated tests cover identity, direct and indirect base-class relationships, interface implementation, value types, variant generics, and representative open-generic BCL semantics; reversed and unrelated failure cases; both null inputs; return-value identity; parameter-name and custom-message propagation; the factory arguments and concrete exception; no factory invocation on success; null-factory behavior; and nullable-flow analysis.
15
+
-[x] No `MustImplement` convenience assertion is added; interface assignability is documented and tested through `MustBeAssignableTo`.
16
+
-[x] The source-export whitelist catalog and committed settings contain `MustBeAssignableTo`, and focused source-export tests cover retention of the guard, its throw helper, and the two-argument custom-exception helper as well as trimming of the exception-factory overload when configured.
17
+
-[x] The committed .NET Standard 2.0 single-file distribution is regenerated with the assertion and validates for both supported source-export targets.
18
+
-[x] The type-relation assertion documentation lists `MustBeAssignableTo`, and the package release notes mention the new guard.
19
+
-[x] The complete solution restores and builds without warnings in Release configuration, and all automated tests pass on the pinned SDK.
20
+
21
+
## Technical Details
22
+
23
+
Add `Check.MustBeAssignableTo.cs` using the conventions of the other value-returning guards (aggressive inlining, JetBrains contract annotations, nullable flow annotations, caller-argument-expression capture, and XML documentation). The exact public shape is:
The check must evaluate `requiredType.IsAssignableFrom(parameter)`. This makes the direction explicit: after success, a value whose runtime type is `parameter` can be stored in a variable declared as `requiredType`. Use the BCL behavior verbatim, including equality, inheritance, interface implementation, array compatibility, and generic variance; do not route through `InheritsFrom`, `IsOrInheritsFrom`, or `IsEquivalentTypeTo`, whose constructed/open-generic handling is intentionally different. No generic `MustBeAssignableTo<T>` overload is included because the motivating scenario supplies the required type at runtime.
41
+
42
+
Route default relation failures through a non-returning `Throw.MustBeAssignableTo` helper that constructs `ArgumentException`. Its default message should state both types and the assignment direction without implying that the `Type` object itself failed a CLR cast. The default overload validates both type arguments before evaluating assignability; the factory overload treats either null as a validation failure and passes both original values to the factory. No trimming annotations or microbenchmarks are required because `Type.IsAssignableFrom` does not enumerate reflected members and this guard is a thin wrapper around the BCL operation.
43
+
44
+
Register `MustBeAssignableTo` in `AssertionWhitelist` and `settings.json`, add focused `SourceFileMergerWhitelistTests`, add `MustBeAssignableToTests` under the type assertions, extend the value-returning assertion coverage in `Issue72NotNullAttributeTests`, update the type-relation table in `docs/assertion-overview.md`, and regenerate `Light.GuardClauses.SingleFile.cs` through the source-export tool's committed settings.
0 commit comments