想象一下,我有这个async
方法:
public async Task Foo(object fooObj)
{
//do foo's stuff.
//maybe set a fooable static global variable
//or write some data to fooObj
}
并称之为:
public async void FooCaller()
{
await Foo(new dummyObject)
}
好的,所以:第一个代码块返回Task
,但它实际上并没有返回任何地方。
我可以想象async
关键字的情况有一些编译器异常,否则需要返回值。
但如果是这种情况,返回值如何初始化? (好吧,我可以自己检查一下)。
更重要的是:
使用此模式时是否存在任何陷阱? (例如:访问fooObj
,延迟执行时间等。)
答案 0 :(得分:8)
好的,所以:第一个代码块返回一个Task,但它实际上并没有返回任何地方。
事实上。您可以将其视为Task<void>
- 如果它是有效的。它用于指示异步方法何时完成。
基本上,任何非void异步方法都会包装您在任务中返回的任何内容。例如:
async Task<string> FooAsync()
{
// Do some async operations
return "foo";
}
请注意,return
语句中的表达式类型为string
,但声明的方法返回类型为Task<string>
。编译器完成所有工作以将异步操作包装在合适的Task
中,该方法在方法完成或抛出异常时完成。 (在这种情况下,返回的Task
具有“故障”状态。)
需要这样做的原因是,在方法完成之前,对异步方法的调用通常返回。将方法调用视为“请开始做某事” - 它将启动并返回,但您想知道“某事”何时完成,以及结果是什么。
await
表达式执行相反的展开 - 它只会在等待操作完成时继续通过该方法,并打开该值(例如使用Task.Result
)。但它不会阻塞,而是会立即返回。这就是组合如何与等待其他异步方法的结果的异步方法很好地协作。
一旦你完成了string
到Task<string>
包装以及等同的解包,如果你await
这个任务,就应该很容易看到{{1}的类比}和Task
。因此,这对void
方法有效:
void
对于void Foo()
{
// Do some actions.
...
// No need for a return statement
}
方法,等价物是正确的:
async Task
你可以在async Task Foo()
{
// Do some actions (presumably using await)
...
// No need for a return statement
}
方法中使用return
语句(没有值):
async Task