2016-11-21 48 views
2

考虑设置一个ICommand执行异步任务的以下两种方式(在这种情况下,使用Xamarin.Forms.Command,但我希望这不是关键):定义ICommand作为异步lambda调用异步任务,或只是异步无效?

方案1:命令设置中,等待异步拉姆达一个async Task方法:

// 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中的异步拉姆达也AFAIK有效async void。)

回答

2

只要因为我们不会直接调用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