【发布时间】:2020-09-25 10:51:37
【问题描述】:
这是我的测试:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace asynktest
{
class Program
{
static async Task Main(string[] args)
{
var t = Test1(true);
await t; //throws InvalidOperationException here. correct
t = Test1(false);
await t; //throws NotImplementedException here. correct
t = Test2(true); //throws InvalidOperationException here. wrong
await t;
t = Test2(false); //throws NotImplementedException here. wrong
await t;
t = Test3(true);
await t; //throws InvalidOperationException here. correct
t = Test3(false); //throws NotImplementedException here. wrong
await t;
t = Test4(true);
await t; //throws InvalidOperationException here. correct
t = Test4(false);
await t; //throws NotImplementedException here. correct
t = Test5(true);
await t; //throws InvalidOperationException here. correct
t = Test5(false);
await t; //throws NotImplementedException here. correct
}
public static async Task<int> Test1(bool err) //CS1998: This async method lacks 'await'
{
if (err)
throw new InvalidOperationException();
return GetNum(42);
}
public static Task<int> Test2(bool err)
{
if (err)
throw new InvalidOperationException();
return Task.FromResult(GetNum(42));
}
public static Task<int> Test3(bool err)
{
if (err)
return Task.FromException<int>(new InvalidOperationException());
return Task.FromResult(GetNum(42));
}
public static async Task<int> Test4(bool err)
{
await Task.CompletedTask; // remove CS1998
if (err)
throw new InvalidOperationException();
return GetNum(42);
}
public static Task<int> Test5(bool err)
{
try
{
if (err)
return Task.FromException<int>(new InvalidOperationException());
return Task.FromResult(GetNum(42));
}
catch (Exception e)
{
return Task.FromException<int>(e);
}
}
public static int GetNum(int num)
{
throw new NotImplementedException();
}
}
}
Test1 生成我要修复的警告。只有 Test4 和 Test5 不会改变程序流程。但是 Test5 需要大量的样板代码。真的是替代方案是在我的程序中撒上“await Task.CompletedTask”或添加大量愚蠢的Task.FromX代码吗? 在这一点上我真的觉得CS1998是完全错误的,必须沉默。 还是我错过了什么?
编辑:删除 Task 作为结果不是一个选项,它通常是一个接口(我无法控制)实现。
Edit2:程序流程是这里的关键。更改程序以在不同的地方抛出异常并不是一件好事。想象一个程序创建 3 个任务,关闭一个核反应堆,然后是 Task.WaitAll(t1, t2, t3)。保留代码原样或 Test4\Test5,否则反应堆会爆炸:-)
Edit3:我经常读到异步创建不必要的状态机(性能),但从我的示例中您可以看到这并不完全正确。如果没有这个状态机,它将改变程序流程。当然,这必须与强制异步接口实现有关,否则您只会使其同步,我想在这里 CS1998 可能会有所帮助:-) 但是 CS1998 不够聪明,无法理解这是您可以控制的代码还是不是。棘手...
Edit4:我最终忽略了警告,但使用包装器的答案是一个很好的选择。但我希望微软可以制作一个补充的“sync”关键字,除了“async”时生成状态机之外,它还可以自动生成这些包装器。
【问题讨论】:
-
当它不做任何异步操作时,为什么要将方法声明为
async? -
@JohnathanBarclay 这可能只是你必须实现的接口。
-
@WiktorZychla 但不是在这里,如果是,请使用
Task.FromResult。 -
Edit: removing Task as result is not an option, it is typically an interface (that I cannot control) implementation.Task.FromResult然后 - 并从函数声明中删除async。要理解的关键是警告正确地指出你正在做的事情很奇怪。有时奇怪的代码是有效的,当然,但通常它是错误的。
标签: c# async-await