为何非泛型Task构造函数可配合async使用,泛型却不行?
首先得明确两个核心点:async委托的实际返回类型,以及Task构造函数的参数签名要求。
非泛型
Task的构造函数接受的是Action类型委托(无返回值的方法)。当你编写无返回值的async () => { ... }lambda时,编译器会将其编译为async void的委托,刚好能匹配Action的签名,因此代码可以正常通过编译(这里要提醒一句:async void仅适合用于事件处理场景,普通异步逻辑用它会导致异常难以捕获,非常不推荐)。泛型
Task<bool>的构造函数要求的是Func<bool>类型委托(直接返回bool值的方法)。但带返回值的async () => { ... return noSolution; }lambda,编译器会自动将其包装为Func<Task> (返回Task的方法),和构造函数需要的 Func<bool>类型完全不兼容,所以无法编译通过。
简单总结:async lambda的返回值会被自动包装成Task<T>,泛型Task构造函数需要的是直接返回T的委托,两者类型不匹配;而非泛型Task构造函数接受无返回值的Action,async void刚好能满足签名要求(尽管这是不推荐的写法)。
正确的替代写法
不管是泛型还是非泛型场景,都不推荐用Task构造函数配合async委托。直接使用Task.Run或者直接调用异步方法返回Task才是标准的异步编程方式,比如泛型场景的正确写法:
var task = Task.Run(async () => { var noSolution = await AsyncMethodWithBoolReturnValue(); if (noSolution) { await DoSomethingAsync(); } return noSolution; });
Task.Run可以直接接受Func<Task<bool>>类型的委托,完美匹配async lambda的类型,能正常工作。
内容的提问来源于stack exchange,提问作者Christian Edel

