【发布时间】:2020-07-16 09:36:49
【问题描述】:
我希望我的 UI 在单击按钮后显示加载叠加层。
选项 1
MyCommand = new DelegateCommand(() => Task.Run(MyLongRunningTask);
private async Task MyLongRunningTask() {
IsLoading = true;
Thread.Sleep(5000);
IsLoading = false;
}
选项 2
MyCommand = new DelegateCommand(MyLongRunningMethod);
private async void MyLongRunningMethod() {
IsLoading = true;
Thread.Sleep(5000);
IsLoading = false;
}
选项 1 有效,选项 2 无效。这种情况我很好,虽然我不明白为什么。 但我真的很想避免将每次执行都包装到 () => Task.Run(...) 中。
由于 async void 似乎是个坏主意,我想继续使用任务。但是我想缩短语法,所以我疯狂地尝试,但我不知道如何将 () => Task.Run(..) 放在派生自 DelegateCommand 的方法或类中。
为什么不编译?
private DelegateCommand GetAsyncCommand(Task t)
{
return new DelegateCommand(() => Task.Run(t));
}
任何与此类似的事情都会导致编译时错误或我不得不调用
MyCommand = GetAsyncCommand(MyLongRunningTask());
因为有 () 大括号,它会在实例化时立即执行我的任务。
这对我来说似乎真的很令人费解。如果我必须将所有长时间运行的任务包装到这个结构中,为什么我不能构建一个更智能的命令来为我完成一个任务?
编辑:我只是注意到选项 1 可能不会锁定 UI,但它会吞下可怕的异常。
如何设置不阻塞 UI 并正常抛出异常的命令?
【问题讨论】:
-
让我引用 Stephen Cleary 的优秀文章 (docs.microsoft.com/en-us/archive/msdn-magazine/2013/march/…):“如果调用者不希望它们是异步的,异步 void 方法可能会造成严重破坏。”
-
他还写了一个AsyncCommand。这会给你一个干净的语法。 github.com/StephenCleary/Mvvm.Async/blob/master/src/…
-
Avoid async void。它仅适用于事件处理程序。
-
方法
MyLongRunningTask应该给你一个警告,你不应该忽略:这个异步方法缺少'await'操作符,并且会同步运行。考虑使用“await”运算符来等待非阻塞 API 调用,或使用“await Task.Run(...)”在后台线程上执行 CPU 密集型工作。 -
“为什么不编译?” 因为
Task.Run()想要Func或Action,但你给它的是Task。编译器应该会告诉你类似的事情。
标签: c# wpf async-await command task