2016-11-21 42 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