这是我的测试:
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
Test1 生成我想要修复的警告。只有 Test4 和 Test5 不会改变程序流程。但是 Test5 需要大量的样板代码。真的可以替代方案是在我的程序中散布“await Task.CompletedTask”或添加疯狂数量的愚蠢Task.FromX代码吗?在这一点上我真的觉得CS1998是完全错误的,必须被静音。或者我错过了什么?
编辑:作为结果删除任务不是一个选项,它通常是一个接口(我无法控制)实现。
Edit2:程序流程是这里的关键。更改程序以在不同的地方抛出异常是不好的。想象一个程序创建 3 个任务,关闭核反应堆,然后 Task.WaitAll(t1, t2, t3)。保留代码原样或 Test4\Test5,否则反应器会爆炸:-)
Edit3:我经常读到 async 创建一个不必要的状态机(性能),但从我的例子中你可以看到这并不完全正确。没有这个状态机,它会改变程序流程。当然,这必须与强制异步接口实现有关,否则你只会让它同步,我想这里 CS1998 可能会有所帮助:-) 但是 CS1998 不够聪明,无法理解这是你可以控制的代码还是不是。棘手...
Edit4:我最终忽略了警告,但是包装器的答案是一个非常好的选择。但我希望微软可以制作一个补充的“同步”关键字,除了在“异步”时生成状态机外,还可以自动生成这些包装器。
删除任务作为结果不是一个选项,它通常是一个接口(我无法控制)实现
使用async关键字创建状态机,如果实现是同步的,则不需要。
如果方法必须返回Task<TResult>以满足接口约定,请使用Task.FromResult().
对于返回非泛型的方法Task,返回Task.CompletedTask。
在异常处理的情况下,使用Task.FromException:
public static Task<int> Test3(bool err)
{
if (err) return Task.FromException<int>(new InvalidOperationException());
try { return Task.FromResult(GetNum(42)); }
catch (Exception e) { return Task.FromException<int>(e); }
}
Run Code Online (Sandbox Code Playgroud)
如果样板的数量是一个问题,那么如何使用这些包装器:
Task RunAsTask(Action action)
{
try { action(); }
catch (Exception e) { return Task.FromException(e); }
return Task.CompletedTask;
}
Task<TResult> RunAsTask<TResult>(Func<TResult> func)
{
try { return Task.FromResult(func()); }
catch (Exception e) { return Task.FromException<TResult>(e); }
}
Run Code Online (Sandbox Code Playgroud)
然后你可以同步编写你的方法:
public static Task<int> Test1(bool err) => RunAsTask(() =>
{
if (err)
throw new InvalidOperationException();
return GetNum(42);
});
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
323 次 |
| 最近记录: |