C# "(沙马林)";异步无效“;框架内覆盖
我有一个从远程服务器检索信息并向用户显示该信息的屏幕。我希望在屏幕显示时更新此信息(不需要额外的用户交互) 这可以接受吗?我已经看到了所有关于“async void”弊病的讨论,以及如何只在事件处理程序中使用它,而这并不是一个很好的事件处理程序 由于我(1)在获取所有异步之前调用了base.OnAppearing(),以及(2)捕获了我的异常,所以在这种情况下,框架是否保证是正常的C# "(沙马林)";异步无效“;框架内覆盖,c#,asynchronous,xamarin,async-await,C#,Asynchronous,Xamarin,Async Await,我有一个从远程服务器检索信息并向用户显示该信息的屏幕。我希望在屏幕显示时更新此信息(不需要额外的用户交互) 这可以接受吗?我已经看到了所有关于“async void”弊病的讨论,以及如何只在事件处理程序中使用它,而这并不是一个很好的事件处理程序 由于我(1)在获取所有异步之前调用了base.OnAppearing(),以及(2)捕获了我的异常,所以在这种情况下,框架是否保证是正常的 protected override OnAppearing(){ base.OnAppearing();
protected override OnAppearing(){
base.OnAppearing();
Task.Run(async()=>{
try
{
// Use VM and Binding...
MyVM.Data = (await GoGetTheData ()).Stuff;
}
catch (Exception e)
{
// Here I think you should use Device.BeginInvokeOnMainThread();
MyVm.Data = "Narg";
}
});
}
在某处
MyText.BindingContext = MyVm;
MyText.SetBinding(Label.TextProperty, "Data");
在处理事件的情况下(在外观上,基本上是显示的回调),这是完全正常的。 返回类型异步方法应返回任务、任务或 空虚 如果该方法不返回任何其他值,请指定任务返回类型 价值观 如果方法需要返回值,请指定任务,其中 TResult是返回的类型(例如int) void返回类型主要用于需要 信息技术调用void返回异步方法的代码不能等待 结果呢 在短时间内尽可能使用任务,但在与事件处理程序相关的情况下只使用void。 如果您需要调用async,那么您可以根据这个问题使用
TaskCompletionSource
private TaskCompletionSource继续选中;
专用异步无效按钮\u单击\u 1(对象发送方,路由目标)
{
//注意:您可能希望在“进行中”时禁用此按钮,以便
//用户不能单击它两次。
等待GetResults();
//在这里重新启用按钮,可能在finally块中。
}
私有异步任务GetResults()
{
//做很多需要很长时间的复杂事情
//(例如,联系一些web服务)
//等待用户单击“继续”。
continueClicked=新建TaskCompletionSource();
按钮continue.Visibility=可见性.Visibility;
等待继续完成任务;
buttonContinue.Visibility=可见性.已折叠;
//更多的工作。。。
}
私有无效按钮继续单击(对象发送者,路由目标)
{
if(continueClicked!=null)
continueClicked.TrySetResult(null);
}
工具书类
假设这是对调用页面显示事件的受保护方法的重写,则这是可以的 此方法实际上是一个事件,它是在引发事件之前调用的方法 编辑:正如@apineda在评论中指出的,下面的情况实际上并非如此
只需注意,异步工作将在任何事件处理程序运行后完成,因为这些事件处理程序是在
base.OnAppearing()中调用的,这是可以接受的。添加这些框架覆盖上的异步支持是有原因的。“async void”通常有一些缺点,但这种情况不适用
但是需要注意的是,您应该确保您的OnAppearing(Forms)
、OnCreate(Android)
和ViewDidLoad(iOS)
方法尽快返回,以便您的用户有一个愉快的体验
事实上,您将在许多表单示例中看到这种确切的语法:此方法如何调用?可以接受,因为这些重写与事件处理程序没有太大区别:它们的存在是为了通知派生类的实例某些事件。但是,您应该跟踪挂起的任务,如GoGetTheData
。如果在上一个任务完成之前再次调用出现的on
,该怎么办?如果您愿意,可以将其视为异步重新进入。我更新了代码,表明这是对最初在Xamarin.Forms.Page中定义的“OnAppearing()”的重写。我知道在人们回答后更新代码通常是不好的,但我相信在这种情况下,我更新的代码不会使任何答案或其他讨论无效。这将引发跨线程异常,因为您无法从后台线程访问UI对象…而且,就使用它的后果而言,它与异步void方法几乎相同。最好的选择是使用“绑定”。他还可以尝试使用Device.beginInvokeMainThread();但我认为用户界面可能会冻结用户界面在任何情况下都不会冻结。Task.Run不用于已经异步的方法。它用于将CPU绑定的工作推送到后台线程,以避免冻结主线程。他在问题中表示,他知道它主要用于事件处理程序。你基本上是在重复他所说的,他已经知道了。这是一个非常详细的回答,但他只是问他的具体用途是否可以接受。他展示的方法似乎是调用页面
的显示
事件的方法,因此本质上是一个事件本身。这意味着在这里使用async void应该是可以的。我在第一次修订中说过“在可能的情况下,简短地使用task,但在与事件处理程序相关的情况下,只使用void。”仅供参考,OnAppearing是一个事件回调。不幸的是,情况并非如此。OnAppearing只是ContentPage父对象中的一个空虚拟方法。在页面的另一个事件中分别调用出现的事件处理程序和此方法。链接:@apineda谢谢你注意到这一点。我已经编辑了我的答案。
MyText.BindingContext = MyVm;
MyText.SetBinding(Label.TextProperty, "Data");
private TaskCompletionSource<object> continueClicked;
private async void Button_Click_1(object sender, RoutedEventArgs e)
{
// Note: You probably want to disable this button while "in progress" so the
// user can't click it twice.
await GetResults();
// And re-enable the button here, possibly in a finally block.
}
private async Task GetResults()
{
// Do lot of complex stuff that takes a long time
// (e.g. contact some web services)
// Wait for the user to click Continue.
continueClicked = new TaskCompletionSource<object>();
buttonContinue.Visibility = Visibility.Visible;
await continueClicked.Task;
buttonContinue.Visibility = Visibility.Collapsed;
// More work...
}
private void buttonContinue_Click(object sender, RoutedEventArgs e)
{
if (continueClicked != null)
continueClicked.TrySetResult(null);
}