有没有这样的写作方法:

public async Task<SomeResult> DoSomethingAsync()
{
    // Some synchronous code might or might not be here... //
    return await DoAnotherThingAsync();
}

而不是这样:

public Task<SomeResult> DoSomethingAsync()
{
    // Some synchronous code might or might not be here... //
    return DoAnotherThingAsync();
}

有意义吗?

为什么使用返回等待构造时,你可以直接返回任务<T>从内部DoAnotherThingAsync()调用?

我在很多地方看到等待返回的代码,我想我可能错过了一些东西。但据我所知,在这种情况下不使用async/await关键字,直接返回任务在功能上是等效的。为什么要增加额外等待层的额外开销?


当前回答

另一个你可能需要等待结果的例子是:

async Task<IFoo> GetIFooAsync()
{
    return await GetFooAsync();
}

async Task<Foo> GetFooAsync()
{
    var foo = await CreateFooAsync();
    await foo.InitializeAsync();
    return foo;
}

在这种情况下,GetIFooAsync()必须等待GetFooAsync的结果,因为两个方法之间的T类型不同,Task<Foo>不能直接分配给Task<IFoo>。但是如果你等待结果,它就变成了Foo,可以直接赋值给IFoo。然后async方法只是在Task<IFoo>中重新打包结果,然后离开。

其他回答

如果你不需要async(即,你可以直接返回Task),那么不要使用async。

在某些情况下,return await是有用的,比如如果你有两个异步操作要做:

var intermediate = await FirstAsync();
return await SecondAwait(intermediate);

有关异步性能的更多信息,请参阅Stephen Toub关于该主题的MSDN文章和视频。

更新:我已经写了一篇博客文章来详细介绍。

普通方法中的return和异步方法中的return await有一个狡猾的情况:当与using(或者更一般地,try块中的任何return await)结合使用时。

考虑一个方法的以下两个版本:

Task<SomeResult> DoSomethingAsync()
{
    using (var foo = new Foo())
    {
        return foo.DoAnotherThingAsync();
    }
}

async Task<SomeResult> DoSomethingAsync()
{
    using (var foo = new Foo())
    {
        return await foo.DoAnotherThingAsync();
    }
}

第一个方法将在DoAnotherThingAsync()方法返回时立即Dispose() Foo对象,这可能远在它实际完成之前。这意味着第一个版本可能有bug(因为Foo被处理得太快了),而第二个版本会工作得很好。

非await方法的另一个问题是有时你不能隐式强制转换返回类型,特别是Task<IEnumerable<T>>:

async Task<List<string>> GetListAsync(string foo) => new();

// This method works
async Task<IEnumerable<string>> GetMyList() => await GetListAsync("myFoo");

// This won't work
Task<IEnumerable<string>> GetMyListNoAsync() => GetListAsync("myFoo");

错误:

不能隐式转换类型` System.Threading.Tasks.Task<System.Collections.Generic。列出>'到'System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable>'

您可能希望返回await的另一个原因是:await语法允许您避免遇到Task<T>和ValueTask<T>类型之间的不匹配。例如,下面的代码即使SubTask方法返回Task<T>,但它的调用者返回ValueTask<T>。

async Task<T> SubTask()
{
...
}

async ValueTask<T> DoSomething()
{
  await UnimportantTask();
  return await SubTask();
}

如果你跳过DoSomething()行上的await,你会得到一个编译器错误CS0029:

不能隐式转换类型` System.Threading.Tasks。Task<BlaBla>'到'System.Threading.Tasks.ValueTask<BlaBla>'。

如果您尝试显式地类型转换它,也会得到CS0030。

顺便说一下,这是。net框架。我完全可以预见到一个评论说“这在。net的hypothetical_version中是固定的”,我还没有测试它。:)

这也让我感到困惑,我觉得之前的回答忽略了你的实际问题:

当你可以直接从内部DoAnotherThingAsync()调用返回任务时,为什么要使用返回等待构造?

有时候你需要Task<SomeType>,但大多数时候你需要SomeType的实例,也就是说,任务的结果。

从你的代码:

async Task<SomeResult> DoSomethingAsync()
{
    using (var foo = new Foo())
    {
        return await foo.DoAnotherThingAsync();
    }
}

不熟悉语法的人(例如我)可能认为这个方法应该返回Task<SomeResult>,但是由于它被标记为async,这意味着它的实际返回类型是SomeResult。 如果你只是使用return foo.DoAnotherThingAsync(),你将返回一个任务,它不会编译。正确的方法是返回任务的结果,因此返回等待。