因此,我一直在进行大量的挖掘工作,但在任何地方都找不到任何真正的确定答案。
我正在使用MVVM Light和WPF编写应用程序。我有一个注入到ViewModel中的服务,用于检查设置的某些属性的有效性。该服务进行网络呼叫,并且是异步的。该调用不需要停止应用程序的执行,而只是向用户提供有关输入值的有效性的视觉反馈。
这样,我设法将某些东西合在一起以使其正常工作,但是感觉有些乱七八糟。
在不使用async void
之类的情况下对属性更改执行异步方法的正确方法是什么?
这是我目前拥有的。
public string OmmaLicenseNumber
{
get => _ommaLicenseNumber;
set
{
Set(() => OmmaLicenseNumber, ref _ommaLicenseNumber, value);
Patient.OmmaLicenseNumber = value;
var _ = CheckLicenseValid();
}
}
private async Task CheckLicenseValid()
{
var valid = await _licenseValidationService.IsValidAsync(OmmaLicenseNumber);
// We don't want the UI piece showing up prematurely. Need 2 properties for this;
LicenseValid = valid;
LicenseInvalid = !valid;
}
如果我只是尝试在async方法上调用.Result
,则会导致死锁,需要重新启动应用程序才能解决。尽管我的作品很有效,但我并不是真正的粉丝。我还有其他选择吗?
答案 0 :(得分:2)
这里的问题不是如何运行未被观察到的async
任务,而是如何处理异常。我之所以这样说,是因为清理任务后它们可能会出现。
理想情况下,只需向下一个读者展示他们所得到的。由于您反对使用async void
选项1
// running an async method unobserved
Task.Run(async () =>
{
try
{
await DoSomethingAsync();
}
catch (Exception e)
{
// Deal with unobserved exceptions
// or there will be dragons
}
});
注意 :从技术上讲,这是卸载的操作(它将失去上下文的注意),而async
lamda使其成为异步无效对象无论如何,但是无论如何您都必须处理例外情况
选项2 ,还有争议的
public async void DoSomethingFireAndForget()
{
try
{
await DoSomethingAsync();
}
catch (Exception e)
{
// Deal with unobserved exceptions
// or the will be dragons
}
}
选项3 两全其美。
注意 :使用需要观察异常的管道,例如Action
public static class TaskUtils
{
#pragma warning disable RECS0165 // Asynchronous methods should return a Task instead of void
public static async void FireAndForgetSafeAsync(this Task task, Action<Exception> onErrors = null)
#pragma warning restore RECS0165 // Asynchronous methods should return a Task instead of void
{
try
{
await task;
}
catch (Exception ex)
{
onErrors?.Invoke(ex);
}
}
}
也许 Stephen Cleary 稍后会在这发生,并为您提供更有说服力的解释
答案 1 :(得分:2)
事件处理程序允许使用异步void
引用Async/Await - Best Practices in Asynchronous Programming
public string OmmaLicenseNumber {
get => _ommaLicenseNumber;
set {
Set(() => OmmaLicenseNumber, ref _ommaLicenseNumber, value);
Patient.OmmaLicenseNumber = value;
//Assuming event already subscribed
//i.e. OmmaLicenseNumberChanged += OmmaLicenseNumberChanged;
OmmaLicenseNumberChanged(this,
new LicenseNumberEventArgs { LicenseNumber = value }); //Raise event
}
}
private event EventHandler<LicenseNumberEventArgs> OmmaLicenseNumberChanged = delegate { };
private async void OnOmmaLicenseNumberChanged(object sender, LicenseNumberEventArgs args) {
await CheckLicenseValid(args.LicenseNumber); //<-- await async method call
}
private async Task CheckLicenseValid(string licenseNumber) {
var valid = await _licenseValidationService.IsValidAsync(licenseNumber);
// We don't want the UI piece showing up prematurely. Need 2 properties for this;
LicenseValid = valid;
LicenseInvalid = !valid;
}
//...
public class LicenseNumberEventArgs : EventArgs {
public string LicenseNumber { get; set; }
}
我认为这有点笨拙吗?
是的。这只是为了说明它是可行的示例。
可以将其重构为一些更简单的帮助程序/实用方法调用吗?
是的。看起来很像一个等待的回调模式,它使用表达式来获取要验证的值
答案 2 :(得分:1)
看看Stephen Cleary编写的NotifyTask
类。
这是在构造函数和属性中处理异步操作的好方法。
您可以将代码重构为:
private NotifyTask _OmmaLicenseNumberChangedNotifyTask
= NotifyTask.Create(Task.CompletedTask);
public NotifyTask OmmaLicenseNumberChangedNotifyTask
{
get => this._OmmaLicenseNumberChangedNotifyTask;
set
{
if (value != null)
{
this._OmmaLicenseNumberChangedNotifyTask = value;
OnPropertyChanged("OmmaLicenseNumberChangedNotifyTask");
}
}
}
public string OmmaLicenseNumber
{
get => _ommaLicenseNumber;
set
{
Set(() => OmmaLicenseNumber, ref _ommaLicenseNumber, value);
Patient.OmmaLicenseNumber = value;
OmmaLicenseNumberChangedNotifyTask = NotifyTask.Create(CheckLicenseValid());
}
}
然后,您可以忘记Task
,也可以将UI元素绑定到NotifyTask
的属性,例如IsCompleted
,只有在{{1 }}已完成。