将ICommand定义为异步lambda调用异步任务,还是只是async void?

时间:2016-11-21 10:23:32

标签: c# async-await icommand

考虑以下两种设置ICommand以执行异步任务的方法(在这种情况下,使用Xamarin.Forms.Command,但我希望这并不重要):

场景1:将命令设置为等待async Task方法的异步lambda:

// Command definition
ToggleCheckedCommand = new Command(
    execute: async () => { await ToggleCheckedAsync(); },
    canExecute: () => !IsBusy);

// Method that is executed
private async Task ToggleCheckedAsync()
{
    IsBusy = true;
    await DoWork();
    IsBusy = false;
}

场景2:将命令设置为async void方法:

// Command definition
ToggleCheckedCommand = new Command(
    execute: ToggleCheckedAsync,
    canExecute: () => !IsBusy);

// Method that is executed
private async void ToggleCheckedAsync()
{
    IsBusy = true;
    await DoWork();
    IsBusy = false;
}

只要一个人永远不会直接打电话给ToggleCheckedAsync,这两个场景是否相同,或者是否有任何一个与另一个相比?

(我知道async void通常被认为是直接事件处理程序之外的不良做法,但ToggleCheckedAsync在逻辑上是一个事件处理程序,方案1中的异步lambda也是AFAIK有效async void 。)

1 个答案:

答案 0 :(得分:3)

  

只要一个人永远不会直接调用ToggleCheckedAsync,这两个场景是否相同,或者是否有任何一个与另一个相比?

任何一个都没事;他们是等同的方法。 async void在这里是合适的,因为ICommand.Execute在逻辑上是一个事件处理程序。 (并且不要搞错:在两种方法中 都是async void:第一个例子的lambda变为async void)。

但是,在我自己的代码中,这并不成立:

  

只要一个人永远不会直接调用ToggleCheckedAsync

特别是单元测试。单元测试可以直接执行您的命令,其中包括能够await完成命令,ICommand无法满足此需求。

因此,我发现公开async Task方法很有用。或者,如果你想获得更高级,请开发IAsyncCommand type with a Task ExecuteAsync method and expose that from the ViewModel。将该设计理解为合乎逻辑的结论,您最终可以使用full AsyncCommand that hides the async void Execute as an implementation detail