我认为在从同步执行的方法(ICommand.Execute)调用异步方法时存在两个主要问题:1)拒绝在上一个调用仍在运行时再次执行;2)异常处理。这两个问题都可以通过像下面这样的实现(原型)来解决。这将是委托命令的异步替代品。
public sealed class AsyncDelegateCommand : ICommand
{
private readonly Func<object, Task> func;
private readonly Action<Exception> faultHandlerAction;
private int callRunning = 0;
public AsyncDelegateCommand(Func<object, Task> func, Action<Exception> faultHandlerAction)
{
this.func = func;
this.faultHandlerAction = faultHandlerAction;
}
public bool CanExecute(object parameter)
{
return callRunning == 0;
}
public void Execute(object parameter)
{
if (Interlocked.CompareExchange(ref callRunning, 1, 0) == 1)
{
return;
}
OnCanExecuteChanged();
func(parameter).ContinueWith((task, _) => ExecuteFinished(task), null, TaskContinuationOptions.ExecuteSynchronously);
}
private void ExecuteFinished(Task task)
{
Interlocked.Exchange(ref callRunning, 0);
if (task.IsFaulted)
{
faultHandlerAction(task.Exception);
}
OnCanExecuteChanged();
}
public event EventHandler CanExecuteChanged;
private void OnCanExecuteChanged()
{
var handler = CanExecuteChanged;
if (handler != null) handler(this, EventArgs.Empty);
}
}
异步无返回值
我个人会尽可能避免使用 "异步无返回值"。从外部无法知道操作何时完成,错误处理也变得复杂。例如,编写一个 "异步任务" 方法被一个 "异步无返回值" 方法调用时,几乎需要知道其失败的任务如何传播:
public async Task SomeLogic()
{
var success = await SomeFurtherLogic();
if (!success)
{
throw new DomainException(..);
}
}
接着,另一天有人写道:
public async void CommandHandler()
{
await SomeLogic();
}
async
方法以及该主题周围存在哪些最佳实践。 - sorosh_sabzasync
方法时,是否有更好的解决方案(或最佳实践)? - sorosh_sabz