进一步测试后,对于大多数用途来说,这个类可能过于复杂了。
尽管有些人投了反对票,
chawala的回答在我的测试中表现良好。
重要的是,方法声明中存在
async
就足以避免阻塞UI线程。因此,在我看来,chawala的答案“没有问题”;不值得那些反对票。
明确一点:显式的
async => await
回答当然完全没有问题,没有任何问题。如果这样做可以让你更加自信,请使用它们。
我的回答旨在使调用站点更加清洁。然而,maxc的第一个评论是正确的:我所做的不再与显式的async => await“完全相同”。到目前为止,我还没有发现任何情况会有影响。无论是否在new Command中使用async/await,在单击按钮多次快速时,所有单击都会排队。我甚至测试了SomeMethod切换到新页面。我尚未发现任何与显式的async/await有任何区别。在我的测试中,此页面上的所有答案都具有相同的结果。
如果您不使用Task结果,并且没有添加任何代码来处理此方法期间发生的任何异常,那么async void与async Task一样有效。
在这个类代码中,请参见我的评论“待定:考虑在此处添加异常处理逻辑”。
换句话说,大多数开发人员编写的代码并没有什么区别。如果这是个问题,那么在他们的
new Command(await () => async SomeMethod());
版本中同样会出现问题。
下面是一个方便的类。使用它可以简化将命令与
async
组合的过程。
如果您有一个像这样的
async
方法(从已接受的答案中复制):
async Task SomeMethod()
{
}
没有这个类,使用
Command
中的
async
方法将会像这样(来自已接受的答案):
resetButtonClickedCommand = new Command(async () => await SomeMethod());
使用类可以使代码更加简洁:
resetButtonClickedCommand = new AsyncCommand(SomeMethod);
结果等同于没有使用这个类时显示的略长的代码行。虽然没有太大的好处,但有一个隐藏杂乱无章的代码和给一个经常使用的概念命名的代码是很好的。
如果有一个带参数的方法,那么这个好处会更加明显:
async Task SomeMethod(object param)
{
// do stuff
}
没有类:
yourCommand = new Command(async (param) => await SomeMethod(param));
使用类(与无参数情况相同;编译器调用适当的构造函数):
yourCommand = new AsyncCommand(SomeMethod);
class AsyncCommand
的定义:
using System;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows.Input;
namespace MyUtilities
{
public class AsyncCommand : ICommand
{
Func<object, Task> _execute;
Func<object, bool> _canExecute;
public AsyncCommand(Func<object,Task> execute, Func<object, bool> canExecute = null, INotifyPropertyChanged notificationSource = null)
{
_execute = execute;
_canExecute = canExecute ?? (_ => true);
if (notificationSource != null)
{
notificationSource.PropertyChanged += (s, e) => RaiseCanExecuteChanged();
}
}
public AsyncCommand(Func<Task> execute, Func<bool> canExecute = null, INotifyPropertyChanged notificationSource = null)
:this(_ => execute.Invoke(), _ => (canExecute ?? (() => true)).Invoke(), notificationSource)
{
}
public bool CanExecute(object param = null) => _canExecute.Invoke(param);
public Task ExecuteAsync(object param = null) => _execute.Invoke(param);
public async void Execute(object param = null)
{
await ExecuteAsync(param);
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
}
关于下面对
async void Execute
的评论。无论是
class Command
还是
interface ICommand
都有
void Execute
方法。与这些兼容意味着具有相同的方法签名-因此通常推荐的
async Task MethodName()
在这里不可行。请参见我的评论中的链接,以了解在此处使用
void
的影响。
resetButtonClickedCommand = new AsyncCommand(SomeMethod);
- ToolmakerSteveICommand
的内置子类,但我发现它在我们公司的代码库中。我添加了一个定义class AsyncCommand
的答案。 - ToolmakerSteve