你如何实现一个异步操作委托方法?

187

一些背景信息

我正在学习Web API技术栈,并试图通过一个名为"Result"的对象封装所有数据,包括SuccessErrorCodes等参数。

然而,不同的方法可能会产生不同的结果和错误代码,但结果对象通常会以相同的方式进行实例化。

为了节省时间并学习C#中的async/await功能,我正在尝试将Web API操作的所有方法体包装在异步操作委托中,但遇到了一点问题...

给出以下类:

public class Result
{
    public bool Success { get; set; }
    public List<int> ErrorCodes{ get; set; }
}

public async Task<Result> GetResultAsync()
{
    return await DoSomethingAsync<Result>(result =>
    {
        // Do something here
        result.Success = true;
        
        if (SomethingIsTrue)
        {
            result.ErrorCodes.Add(404);
            result.Success = false;
        }
    }
}

我想编写一个方法,对Result对象执行操作并返回它。通常通过同步方法实现:

public T DoSomethingAsync<T>(Action<T> resultBody) where T : Result, new()
{
    T result = new T();
    resultBody(result);
    return result;
}

但是我该如何使用 async/await 将这个方法转换成异步方法呢?

这是我尝试过的代码:

public async Task<T> DoSomethingAsync<T>(Action<T, Task> resultBody) 
    where T: Result, new()
{
    // But I don't know what do do from here.
    // What do I await?
}

1
如果你在实例化T,为什么你的方法需要是异步的?据我所知,在使用异步API的代码中,你只需要从你使用的其他方法传播"异步性"。 - millimoose
抱歉,我还是比较新手,你说的“只需要传播”是什么意思?而且,new T 是什么意思呢? - Albin Anke
我想我弄明白了,谢谢millimoose,你给了我一些思考的东西。 - Albin Anke
1
你为什么要尝试使用异步操作呢?在 Web 服务器的情况下,更常见的情况是通过将同步代码包装在任务中来进行伪异步操作(就像你正在尝试做的那样),这样反而比直接同步执行更慢。 - Scott Chamberlain
1
@AlbinAnke 通过“传播”,我指的是如果您在方法中调用类似于Stream.ReadAsync()的.NET方法,则该方法本身应为异步,并返回一个Task<T>,其中T是如果该方法同步,您将返回的内容。这样做的想法是,因此您的每个方法的调用者都可以“异步等待”(我不知道这个术语的好处),以完成底层的Stream.ReadAsync()。您可以使用的这种情况的比喻是,异步是“具有传染性的”,并从低级内置I/O蔓延到其他代码,其结果取决于所述I/O的结果。 - millimoose
2个回答

406

Action<T>async 等效形式是 Func<T, Task>,因此我认为这就是您要找的内容:

public async Task<T> DoSomethingAsync<T>(Func<T, Task> resultBody)
    where T : Result, new()
{
  T result = new T();
  await resultBody(result);
  return result;
}

@JuanPabloGomez:我不熟悉他们的消息传递方式,但我不认为它不会起作用。 - Stephen Cleary
1
太棒了!我以为不可能创建一个异步的Action,甚至认为这是语言上的缺陷。我没有想到可以使用Func。谢谢。 - Noel Widmer
8
void 方法的异步等效方法是返回 Task 的方法;因此,Action 的异步等效方法是 Func<Task>Action<T> 的异步等效方法是 Func<T, Task>。更多信息请参见此处 - Stephen Cleary
2
@DFSFOT:当异步方法没有返回值时,应该返回Task。如果使用了async关键字,则实际的Task实例将由状态机创建,而不是函数直接创建。 - Stephen Cleary
3
@JohnB: 不是的;Func<T, Task> 是一个接受类型为 T 的参数并且没有返回值的异步方法。Func<Task<T>> 是一个不带参数并返回类型为 T 的值的异步方法。 - Stephen Cleary
显示剩余9条评论

-15

所以我认为实现这个的方法是:

public Task<T> DoSomethingAsync<T>(Action<T> resultBody) where T : Result, new()
{
    return Task<T>.Factory.StartNew(() =>
    {
        T result = new T();
        resultBody(result);
        return result;
    });
}

8
еңЁASP.NETдёӯеә”йҒҝе…ҚдҪҝз”ЁTask.RunпјҲзү№еҲ«жҳҜStartNewпјүгҖӮ - Stephen Cleary
有没有更好的方法来做这件事? - Albin Anke
我发布了一个答案,并且也点赞了@svick的答案。它们都是很好的答案。 - Stephen Cleary

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接