Convert asynchronous action to asynchronous function delegate, preserving synchronous exception delivery

I want to convert asynchronous action delegates to asynchronous function delegates that return a specified value. I have come up with an extension method for this:

public static Func<Task<TResult>> Return<TResult>(this Func<Task> asyncAction, TResult result)
{
    ArgumentValidate.NotNull(asyncAction, nameof(asyncAction));

    return async () =>
    {
        await asyncAction();
        return result;
    };
}

However, my extension method is buggy in that exceptions that would have been delivered synchronously from the action delegate now get delivered asynchronously from the function delegate. Concretely:

Func<Task> asyncAction = () => { throw new InvalidOperationException(); };
var asyncFunc = asyncAction.Return(42);
var task = asyncFunc();   // exception should be thrown here
await task;               // but instead gets thrown here

Is there a way of creating this wrapper in such a way that synchronous exceptions continue to delivered synchronously? Is ContinueWith the way to go?

Update: A concrete example of an asynchronous operation that throws exceptions synchronously:

public static Task WriteAllBytesAsync(string filePath, byte[] bytes)
{
    if (filePath == null)
        throw new ArgumentNullException(filePath, nameof(filePath));
    if (bytes == null)
        throw new ArgumentNullException(filePath, nameof(bytes));

    return WriteAllBytesAsyncInner(filePath, bytes);
}

private static async Task WriteAllBytesAsyncInner(string filePath, byte[] bytes)
{
    using (var fileStream = File.OpenWrite(filePath))
        await fileStream.WriteAsync(bytes, 0, bytes.Length);
}

Test:

Func<Task> asyncAction = () => WriteAllBytesAsync(null, null);
var asyncFunc = asyncAction.Return(42);
var task = asyncFunc();   // ArgumentNullException should be thrown here
await task;               // but instead gets thrown here
Jon Skeet
people
quotationmark

Well, you won't be able to use async in the initial call. That much is clear. But you can use a synchronous delegate which calls the function, and then captures the returned task to await it inside an async delegate:

public static Func<Task<TResult>> Return<TResult>(this Func<Task> asyncAction, TResult result)
{
    ArgumentValidate.NotNull(asyncAction, nameof(asyncAction));

    return () =>
    {
        // Call this synchronously
        var task = asyncAction();
        // Now create an async delegate for the rest
        Func<Task<TResult>> intermediate = async () => 
        {
            await task;
            return result;
        };
        return intermediate();
    };
}

Alternatively, refactor it into two methods, basically extracting the asynchronous lambda expression into an async method:

public static Func<Task<TResult>> Return<TResult>(
    this Func<Task> asyncAction, TResult result)
{
    ArgumentValidate.NotNull(asyncAction, nameof(asyncAction));

    return () =>
    {
        var task = asyncAction();
        return AwaitAndReturn(task, result);
    };
}

public static async Func<Task<TResult>> AwaitAndReturn<TResult>(
    this Task asyncAction, TResult result)
{
    await task;
    return result;
}

people

See more on this question at Stackoverflow