2

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

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

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

您可以选择添加 async 关键字,具体取决于您是否要等待加载某些数据。LoadState但是由于is的返回类型void,LoadState 方法本身不能被等待,而是被触发为“一劳永逸”。这通常很好,因为它允许响应式 UI,同时能够在数据加载逻辑中使用 async/await。

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

4

4 回答 4

4

方法有几个问题async void

  • 它不是可组合的;您无法await完成(如您所述)。
  • 它不容易测试(出于同样的原因)。
  • 错误处理完全不同;我听说过async void“一劳永逸”,但我自己也使用过“火灾和崩溃”这个词。

此外,您永远不应该nullAsync方法返回。这会让其他习惯于TAP的程序员感到惊讶。

您可以提供两种方法void LoadStateTask LoadStateAsync,但如果您这样做,我建议您始终提供两种实现。将异步方法包装在同步方法中或将同步方法包装在异步方法中并没有一种简单的好方法,因此您实际上最终会得到两个几乎相同的实现。

出于维护原因,我不是特别喜欢这个。就个人而言,我更喜欢可能的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.

于 2013-01-07T01:38:19.157 回答
0

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

于 2013-01-06T16:38:04.910 回答
0

您可以将逻辑实现为Task-returning 方法并LoadState丢弃返回值。

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

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

于 2013-01-06T16:38:28.590 回答
0

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

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

virtual void SomeOtherLogic(){}

在调用 LoadState 的地方,现在有 2 个方法调用:

LoadState();
await LoadStateAsync();

SomeOtherLogic();

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

缺点是您(和其他开发人员)始终必须注意/应该使用哪种方法。

于 2013-01-06T17:12:50.847 回答