我已经async
实现了ICommand
接口,当然必须实现方法void Execute(object parameter)
然后实际实现如下所示:
public async void Execute(object parameter)
{
await ExecuteAsync((T)parameter);
}
然后将ExecuteAsync
方法定义如下:
private readonly Func<T, Task> execute;
private bool isExecuting;
public async Task ExecuteAsync(T parameter)
{
try
{
isExecuting = true;
InvokeCanExecuteChanged();
await execute(parameter);
}
finally
{
isExecuting = false;
InvokeCanExecuteChanged();
}
}
现在我知道除了EventHandlers void
之外应该避免作为异步方法的返回类型。但是ICommand
或多或少是一个将视图模型与实际事件处理程序分开的包装器。所以我对这个实现很好还是会遇到问题?
特别是我想知道我是否可以安全地使用execute命令并依赖Task在处理程序之前完成,或者处理程序是否完成而不管Task的状态如何?
答案 0 :(得分:2)
async + void的最大问题是,与普通的void不同,在async void方法中的代码实际完成之前,将执行调用站点上的任何后续代码。你必须100%意识到这一点。
这种行为就是为什么与异步任务相比,async void在API级别上并不常见。事实上,这就是为什么我在公司内部使用async void编写编译器错误的原因 - 并非所有开发人员都知道这一点,如果您希望在调用站点代码继续之前完成该void方法的内容,它可能会引入潜在的错误。
因此,如果命令提供了命令的公共异步Task ExecuteAsync版本,那么您的命令就可以了。
请参阅此示例:
public class SampleCommand<T> : ICommand where T : class
{
/// <inheritdoc />
public SampleCommand(Func<T, Task> execute)
{
this.execute = execute;
}
/// <inheritdoc />
public bool CanExecute(object parameter)
{
return !isExecuting;
}
/// <inheritdoc />
public async void Execute(object parameter)
{
await ExecuteAsync(parameter as T);
}
/// <inheritdoc />
public event EventHandler CanExecuteChanged;
private readonly Func<T, Task> execute;
private bool isExecuting;
public async Task ExecuteAsync(T parameter)
{
try
{
isExecuting = true;
InvokeCanExecuteChanged();
await execute(parameter);
}
finally
{
isExecuting = false;
InvokeCanExecuteChanged();
}
}
private void InvokeCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
public class SampleViewModel
{
public SampleCommand<object> TaskedCommand { get; set; }
public SampleViewModel()
{
TaskedCommand = new SampleCommand<object>(TaskExecutionAsync);
RunSomeMoreInitialization();
RunSomeMoreInitialization2();
}
private async void RunSomeMoreInitialization()
{
/*
* wpf usually calls it this way
* if a user calls this he might not be aware of the different behavior of this void method
*/
TaskedCommand.Execute(null);
await Task.Delay(250);
Debug.WriteLine("more code executed");
/*
* this will print
*
* more code executed
* command invoked
*/
}
private async void RunSomeMoreInitialization2()
{
// user might manually call it this way.
await TaskedCommand.ExecuteAsync(null);
await Task.Delay(250);
Debug.WriteLine("more code executed");
/*
* this will print
*
* command invoked
* more code executed
*/
}
private Task TaskExecutionAsync(object o)
{
Task.Delay(500);
Debug.WriteLine("command invoked");
return Task.CompletedTask;
}
}