但是,如何在同步的PropertyChanged事件处理程序中执行异步操作(例如加载一些数据)而不使用“async void”'hack'呢?
提前感谢!
编辑
我需要避免“async void”的主要原因是因为我在测试驱动环境中工作。异步void方法无法进行测试 :(
实际上,这与 async void
无关。
通常情况下,您希望触发异步操作并让属性设置器返回。示例代码片段:
private string carManufacturerFilter;
public string СarManufacturerFilter
{
get { return carManufacturerFilter; }
set
{
if (carManufacturerFilter != value)
{
carManufacturerFilter = value;
OnPropertyChanged();
// fire async operation and forget about it here;
// you don't need it to complete right now;
var _ = RefreshCarsListAsync();
}
}
}
private async Task RefreshCarsListAsync()
{
// call some data service
var cars = await someDataService.GetCarsAsync(carManufacturerFilter)
.ConfigureAwait(false);
// ...
}
P.S. 我强烈建议您查看Reactive UI。
var _ =
)可以抑制它。在现实生活中,这段代码将被包装成类似于AsyncCommand
的东西。这只是一个示例,旨在为OP提供基本思路。这不是生产就绪的代码。 - Dennisasync void
时,当任务无法等待时,我应该如何断言任何内容? - ˈvɔləasync void
能够被支持是为了允许在通常为void的事件处理程序中使用await
。如果你想要进行测试,可以将整个代码编写在另一个async Task
方法中,并直接调用事件处理程序。在测试中测试这个方法。 void OnPropertyChanged(PropertyChangedEventArgs e)
{
OnPropertyChangedAsync(e)
}
// Test this method
async Task OnPropertyChangedAsync(PropertyChangedEventArgs e)
{
...
}
虽然有点晚了,但我想说一下。首先,注意到INotifyPropertyChanged
表示你的视图模型将实现该接口。
public event PropertyChangedEventHandler? PropertyChanged;
PropertyChangedEventHandler
,您会发现它是一个委托
:public delegate void PropertyChangedEventHandler(object? sender, PropertyChangedEventArgs e);
为什么不使用 Func<>
实现自己的委托呢?例如,如果你想坚持使用 (object? sender, PropertyChangedEventArgs e)
参数模式,那么可以实现类似以下的内容:
public Func<object?, PropertyChangedEventArgs, Task>? PropertyChangedAsync { get; set; }
在同一位置(或替代)public event PropertyChangedEventHandler? PropertyChanged;
。
从那里,您可以像处理PropertyChanged
一样“连接它”:
ViewModel.PropertyChanged += OnViewModelPropertyChanged;
ViewModel.PropertyChangedAsync += OnViewModelPropertyChangedAsync;
private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
// Do something (synchronously) with the notification
}
private Task OnViewModelPropertyChangedAsync(object? sender, PropertyChangedEventArgs e)
{
// Do something (asynchronously) with the notification
return Task.CompletedTask;
}
顺便说一下,如果你想要更高级的操作,你也可以创建一个自定义的 PropertyChangedEventHandler
事件,其接口与 INotifyPropertyChanged
非常相似。显然,唯一的区别是你需要返回 Task
或者 Task<T>
而不是 void。我上面提到的使用 Func<object?, PropertyChangedEventArgs, Task>
的解决方案只是更加简洁(只有一行代码)。
不行。 INotifyPropertyChanged
不支持异步调用。你需要进行一些黑科技,或者重新考虑你的策略。
INotifyPropertyChanged
不适用于异步操作。它的目标是使一个类通知 UI 其数据已更改。UI 在专用线程中工作,因此必须避免跨线程操作。
你应该使用“可怕的”async void
方法。
你也可以使用 Dispatcher.BeingInvoke (async () => { … await …} )
,但这与使用 async void
相同。
async void
与事件处理程序不是一种hack。这是首选的解决方案。事件处理程序本质上是“fire and forget”,因此不应该期望事件处理程序返回任何值。 - FCin