简体   繁体   中英

Define ICommand as async lambda calling async Task, or just async void?

Consider the following two ways of setting an ICommand to perform an asynchronous task (in this case, using Xamarin.Forms.Command , but I expect that's not critical):

Scenario 1: setting the command to an async lambda in which you await an async Task method:

// 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;
}

Scenario 2: Setting the command to an async void method:

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

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

As long as one is never calling ToggleCheckedAsync directly, are these two scenarios equivalent, or are there any gotchas with one compared to the other?

(I know async void is generally considered a bad practice outside direct event handlers, but ToggleCheckedAsync is logically an event handler, and the async lambda in scenario 1 is also AFAIK effectively async void .)

As long as one is never calling ToggleCheckedAsync directly, are these two scenarios equivalent, or are there any gotchas with one compared to the other?

Either one is fine; they're equivalent approaches. async void is appropriate here because ICommand.Execute is logically an event handler. (And make no mistake: there is an async void in both approaches: the lambda of the first example becomes async void ).

However, in my own code, this doesn't hold true:

As long as one is never calling ToggleCheckedAsync directly

In particular, with unit tests. Unit tests can execute your commands directly, and that includes being able to await until they complete, and ICommand doesn't satisfy this need.

So, I find it's useful to expose the async Task methods. Or, if you want to get fancier, develop an IAsyncCommand type with a Task ExecuteAsync method and expose that from the ViewModel . Taking that design to its logical conclusion, you can end up with a full AsyncCommand that hides the async void Execute as an implementation detail .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM