Table of Contents

Class AwaitableResult

Namespace
Dynamitey
Assembly
Dynamitey.Community.dll

Wraps a Task - specifically a Task<TResult> or a ValueTask<TResult> whose TResult is not accessible to the calling assembly - so that await, including a dynamically-bound await on the dynamic result of InvokeMember(object, String_OR_InvokeMemberName, params object?[]), can complete without the C# runtime binder ever needing to know TResult.

public sealed class AwaitableResult
Inheritance
AwaitableResult
Inherited Members

Remarks

await on a dynamic expression compiles to dynamic invocations of GetAwaiter, IsCompleted and GetResult, resolved by the C# runtime binder in the CALLING assembly's accessibility context. When the awaited Task<TResult>'s TResult is not visible to the caller, the binder cannot produce a value of that type, GetResult binds to a void-returning form instead, and the compiler-generated conversion of the await expression's result throws RuntimeBinderException ("Cannot implicitly convert type 'void' to 'object'").

This wrapper sidesteps that failure: every member the await pattern needs - GetAwaiter(), IsCompleted, GetResult() - is a public member with a public signature. GetResult() in particular is declared to return object, never TResult, so the runtime binder can resolve it regardless of whether the caller can see TResult. Internally, the wrapper observes the underlying task through its statically-typed, non-generic Task base (so no binder or generic instantiation ever touches TResult) and then reads the result value out via reflection, which is not subject to accessibility checks. The caller still gets the real result instance back - just boxed as object instead of typed as TResult.

InvokeMember(object, String_OR_InvokeMemberName, params object?[]) returns this wrapper when the invoked member's result is a Task<TResult> or a ValueTask<TResult> whose TResult is not visible outside its declaring assembly (see IsVisible, which - unlike IsPublic - correctly treats a public type nested inside another public type as visible). An inaccessible ValueTask<TResult> is converted with AsTask() first so this wrapper stays Task-based. Every other result - a plain Task, or a Task<TResult> / ValueTask<TResult> whose TResult is visible to callers - passes through unwrapped and behaves exactly as before.

Constructors

AwaitableResult(Task)

Wraps task for accessibility-safe awaiting.

public AwaitableResult(Task task)

Parameters

task Task

The task to wrap.

Exceptions

ArgumentNullException

task is null.

Properties

Task

The underlying task this wrapper awaits. Use this when you need the real Task itself - e.g. to pass it to AwaitResult(object?), inspect IsFaulted, or hand it to other task-based code.

public Task Task { get; }

Property Value

Task

Methods

GetAwaiter()

Returns the awaiter used by await. Not meant to be called directly - the compiler (or, for a dynamically-typed await, the C# runtime binder) calls this as part of the await pattern.

public AwaitableResult.Awaiter GetAwaiter()

Returns

AwaitableResult.Awaiter

See Also