在Task或void方法之间选择异步代码

时间:2013-01-06 16:30:43

标签: c# windows-8 .net-4.5 async-await

我使用LoadState方法作为示例,但这可以看作是异步编程的一般场景。

LoadStateSaveState方法实现通常具有以下签名:

public override void LoadState(..)
public async override void LoadState(..)

您可以选择添加async关键字,具体取决于您是否要等待加载某些数据。但由于LoadState的返回类型为void,因此无法等待LoadState方法本身,并将其作为fire和forget触发。这通常很好,因为它允许响应式UI,同时能够在数据加载逻辑中使用async / await。

在某些情况下,我想等待异步LoadState方法。但是,如果我将签名更改为Task而不是void,那么所有不使用异步逻辑的实现都必须返回null(这似乎不是最佳解决方案)。是否有另一种可能的解决方案,其中可以等待一些方法调用,并且大多数都会停止并忘记(默认情况下)?

4 个答案:

答案 0 :(得分:3)

async void方法有几个问题:

  • 它不可组合;你不能await完成它(如你所说)。
  • 它不易测试(出于同样的原因)。
  • 错误处理完全不同;我听说async void被称为“火与忘记”,但我自己也用过“火灾和崩溃”这个词。

此外,您永远不应该从null方法返回Async。对于习惯TAP的其他程序员而言,这将是令人惊讶的。

您可以提供两种方法void LoadStateTask LoadStateAsync,但如果您这样做,我建议您始终提供这两种方法。 是一个很好的简单方法来包装asynchronous methods in synchronous methodssynchronous methods in asynchronous methods,所以你实际上最终会得到两个几乎相同的实现。

出于维护原因,我并不特别喜欢这个。就个人而言,我更愿意让 - async方法始终具有await - 兼容签名:

interface IWhatever
{
  Task LoadStateAsync();
}

如果实现是同步的,则实现应该是异步的或快速的。同步实现可以使用Task.FromResult

class Whatever : IWhatever
{
  public Task LoadStateAsync()
  {
    ... // fast-running synchronous code
    return Task.FromResult<object>(null);
  }
}

消费代码始终为async

Whatever whatever = ...;
await whatever.LoadStateAsync();

请注意,如果实现是同步的,则消费代码将不会屈服于其调用者;它将继续通过await

同步运行

答案 1 :(得分:0)

我认为有一个Loaded事件在加载完成时会被触发。也许你可以在Loaded事件的事件处理程序中做任何想要继续的事情吗?

答案 2 :(得分:0)

您可以将您的逻辑实现为Task - 返回方法,让LoadState只丢弃返回值。

public override void LoadState() { LoadStateInner(); }
Task LoadStateInner() { await ...; }

现在,您可以在同时履行继承合同的同时调用这两种变体。

答案 3 :(得分:0)

我看到的一个可能的解决方案是在基类中定义2个方法(以及额外逻辑的第3个方法):

virtual void LoadState(){}
virtual async Task LoadStateAsync{ }

virtual void SomeOtherLogic(){}

您将调用LoadState,现在有2个方法调用:

LoadState();
await LoadStateAsync();

SomeOtherLogic();

在此解决方案中,如果SomeOtherLogic为空或不需要来自LoadState方法的数据,则可以覆盖LoadState方法。如果确实需要数据,则在执行SomeOtherLogic之前等待LoadStateAsync方法。

缺点是你(和其他开发者)总是要注意使用哪种方法。