DotNext by Roman Sakno

<PackageReference Include="DotNext" Version="2.1.0" />

 DynamicTaskAwaitable

public struct DynamicTaskAwaitable
Represents dynamically-typed task.
using DotNext.Runtime.CompilerServices; using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading.Tasks; namespace DotNext.Threading.Tasks { [StructLayout(LayoutKind.Auto)] [System.Runtime.CompilerServices.NullableContext(1)] [System.Runtime.CompilerServices.Nullable(0)] public readonly struct DynamicTaskAwaitable { [StructLayout(LayoutKind.Auto)] [System.Runtime.CompilerServices.Nullable(0)] public readonly struct Awaiter : IFuture, INotifyCompletion { private readonly Task task; private readonly ConfiguredTaskAwaitable.ConfiguredTaskAwaiter awaiter; public bool IsCompleted => awaiter.IsCompleted; internal Awaiter(Task task, bool continueOnCaptureContext) { this.task = task; awaiter = task.ConfigureAwait(continueOnCaptureContext).GetAwaiter(); } public void OnCompleted(Action continuation) { awaiter.OnCompleted(continuation); } public dynamic GetResult() { return DynamicTaskAwaitable.GetResult(task); } } private static readonly CallSite<Func<CallSite, Task, object>> GetResultCallSite = CallSite<Func<CallSite, Task, object>>.Create(new TaskResultBinder()); private readonly Task task; private readonly bool continueOnCaptureContext; internal DynamicTaskAwaitable(Task task, bool continueOnCaptureContext = true) { this.task = task; this.continueOnCaptureContext = continueOnCaptureContext; } public DynamicTaskAwaitable ConfigureAwait(bool continueOnCaptureContext) { return new DynamicTaskAwaitable(task, continueOnCaptureContext); } public Awaiter GetAwaiter() { return new Awaiter(task, continueOnCaptureContext); } internal static dynamic GetResult(Task task) { return GetResultCallSite.Target(GetResultCallSite, task); } } }