【问题标题】:Convert asynchronous action to asynchronous function delegate, preserving synchronous exception delivery将异步操作转换为异步函数委托,保留同步异常传递
【发布时间】:2023-03-13 00:07:02
【问题描述】:

我想将异步操作委托转换为返回指定值的异步函数委托。我为此想出了一个扩展方法:

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

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

但是,我的扩展方法有问题,因为本来可以从操作委托同步传递的异常现在从函数委托异步传递。具体来说:

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

有没有办法以同步异常继续同步传递的方式创建这个包装器? ContinueWith 是要走的路吗?

更新:同步抛出异常的异步操作的具体示例:

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);
}

测试:

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

【问题讨论】:

  • 你能提供一个更具体的例子吗?
  • 不清楚为什么您希望同步抛出异常。如果你调用asyncAction() 也不会抛出异常——它会返回一个错误的任务。
  • @JonSkeet:你是对的;我的示例中有一个小错误(匿名函数不应该被标记为async)。这个问题现在有意义吗?
  • @Douglas:嗯。只是因为你的 Func&lt;Task&gt; 实际上并没有做 anything 异步。大概通常它是实际使用 async/await 的代码,只是碰巧抛出了异常?到那时,你会再次调用asyncAction() 而不是抛出。
  • @DanielA.White:有关使用此功能的具体示例,请查看my answer here 的最后一个代码 sn-p(在“更新”下)中的第三个重载。

标签: c# .net asynchronous async-await task


【解决方案1】:

那么,您将无法在初始呼叫中使用async。这很清楚。但是您可以使用调用函数的同步委托,然后捕获返回的任务以在异步委托中等待它:

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();
    };
}

或者,将其重构为两个方法,基本上是将异步 lambda 表达式提取为异步方法:

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;
}

【讨论】:

    猜你喜欢
    • 2021-05-23
    • 2021-10-31
    • 2015-06-25
    • 2017-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-27
    相关资源
    最近更新 更多