diff --git a/src/DotNext.Tests/DelegateHelpersTests.cs b/src/DotNext.Tests/DelegateHelpersTests.cs index 8c6ba50df..eb29ba126 100644 --- a/src/DotNext.Tests/DelegateHelpersTests.cs +++ b/src/DotNext.Tests/DelegateHelpersTests.cs @@ -521,6 +521,28 @@ public static void ToAsync2() func = new Action(static _ => throw new Exception()).ToAsync(); True(func.Invoke(42, new(canceled: false)).IsFaulted); } + + [Fact] + public static void ToAsync3() + { + var func = new Action(static (_, _) => { }).ToAsync(); + True(func.Invoke(42, 42, new(canceled: false)).IsCompletedSuccessfully); + True(func.Invoke(42, 42, new(canceled: true)).IsCanceled); + + func = new Action(static (_, _) => throw new Exception()).ToAsync(); + True(func.Invoke(42, 42, new(canceled: false)).IsFaulted); + } + + [Fact] + public static async Task ToAsync4() + { + var func = new Func(static (x, y) => x + y).ToAsync(); + Equal(84, await func.Invoke(42, 42, new(canceled: false))); + True(func.Invoke(42, 42, new(canceled: true)).IsCanceled); + + func = new Func(static (_, _) => throw new Exception()).ToAsync(); + await ThrowsAsync(func.Invoke(42, 42, new(canceled: false)).AsTask); + } [Fact] public static void HideReturnValue1() diff --git a/src/DotNext/DelegateHelpers.cs b/src/DotNext/DelegateHelpers.cs index 657137d58..8c9dc7aa4 100644 --- a/src/DotNext/DelegateHelpers.cs +++ b/src/DotNext/DelegateHelpers.cs @@ -162,6 +162,82 @@ private static ValueTask Invoke(this Action action, CancellationToken token) return task; } + /// + /// Converts action to async delegate. + /// + /// The type of the first argument to be passed to the action. + /// The type of the second argument to be passed to the action. + /// Synchronous action. + /// The asynchronous function that wraps . + /// is . + public static Func ToAsync(this Action action) + { + ArgumentNullException.ThrowIfNull(action); + + return action.Invoke; + } + + private static ValueTask Invoke(this Action action, T1 arg1, T2 arg2, CancellationToken token) + { + ValueTask task; + if (token.IsCancellationRequested) + { + task = ValueTask.FromCanceled(token); + } + else + { + task = ValueTask.CompletedTask; + try + { + action.Invoke(arg1, arg2); + } + catch (Exception e) + { + task = ValueTask.FromException(e); + } + } + + return task; + } + + /// + /// Converts function to async delegate. + /// + /// The type of the first argument to be passed to the action. + /// The type of the second argument to be passed to the action. + /// The type of the return value. + /// Synchronous action. + /// The asynchronous function that wraps . + /// is . + public static Func> ToAsync(this Func func) + { + ArgumentNullException.ThrowIfNull(func); + + return func.Invoke; + } + + private static ValueTask Invoke(this Func func, T1 arg1, T2 arg2, CancellationToken token) + { + ValueTask task; + if (token.IsCancellationRequested) + { + task = ValueTask.FromCanceled(token); + } + else + { + try + { + task = new(func.Invoke(arg1, arg2)); + } + catch (Exception e) + { + task = ValueTask.FromException(e); + } + } + + return task; + } + /// /// Creates a delegate that hides the return value. ///