一个不做任何事情的异步方法

32

我有一个接口IAnimation,它公开了一个方法BeginAsync()。该方法应该在开始动画时返回并在完成时结束。

我想要做的是实现一个null动画类NoAnimation,当它执行BeginAsync()时,它只会返回。

这是否是正确的实现?

public async Task BeginAsync()
{
    await Task.Run(() => { });
}

我觉得有比这个更优雅的方法。我也考虑过创建一个空方法,但是那样会给我一个我不喜欢的警告。


2个回答

63

只需使用 Task.CompletedTask 返回一个已完成的任务:

public Task BeginAsync()
{
     return Task.CompletedTask;
}

如果你有一个 Task<TResult>,可以使用Task.FromResult<TResult>返回一个已完成的带结果的任务:

public Task<bool> BeginAsync()
{
     return Task.FromResult(true);
}

您当前的实现非常低效,因为它会构建状态机,并使用线程池线程来运行空任务。


3
第一个方法应该是:await Task.CompletedTask;。第二个方法应该是 return await Task.FromResult(true); 此外,它们需要使用 async 关键字。请修复您的代码。 - Gianpiero
13
不,那是非常糟糕的建议。在不需要使用"await"时使用它不会带来任何好处,反而会增加额外的开销。我的做法是正确的。 - Reed Copsey
请注意,此属性仅适用于 .net 4.6 及以上版本。 - C.List
这个有没有一行代码可以解决? - cool breeze
2
@coolbreeze Task.CompletedTaskTask.FromResult 都已经是一行代码了,是吗? - Reed Copsey
显示剩余2条评论

2

我知道这并不完全回答原始问题(或者至少不是以一种其他答案没有的方式)。它为@ReedCopsey的答案提供了额外的上下文,对于那些(包括我!和其他人,根据那个答案的评论)最初可能会发现这里发生了什么,这很有帮助。


很容易陷入这样的思维误区:认为async必须是任何异步方法签名的一部分。看起来好像它必须是;它(误导性地!)读起来好像它的唯一目的就是标记一个方法为异步。

但实际上不是这样的,需要的是TaskTask<T>,而不是async。这在@ReedCopsey的答案以及以下正确的示例中得到展示(稍作修改,来自这个有用的SO答案):

public Task<int> MethodTaskAsync(int arg0, int arg1)
{
    Task<int> task = new Task<int>(() => Method(arg0, arg1));
    task.Start(); // Hot task (started task) should always be returned.
    return task;
}

这就是为什么接口方法签名不需要,也不能有async关键字的原因:一个“异步”方法本身,并不关心它如何被实现,它只是返回一个已启动(可能已经完成)的TaskTask<T>

async并没有将方法标记为异步,那它的作用是什么呢?这是一种更简单的编写异步方法的方式:一旦你将其应用于一个方法,你可以使用await关键字告诉编译器非常简单地处理子异步调用(如果没有它,你可以尝试手动处理子任务,使用像代码块中那样的大量代码,但这样做会非常复杂和容易出错)。

因此,如果你只想从“异步”(即返回Task的)方法返回一个无操作的已完成任务,你可以并且应该同步地这样做,就像@ReedCopsey的回答中使用Task.CompletedTaskTask.FromResult(...) 一样。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接