与lambdas的警报
简化回调的一种显而易见的方法是使用lambda函数。 AlertLambdas程序证明了这一点。 XAML文件与AlertCallbacks方法中的相同,但响应按钮单击而发生的所有内容现在都在Clicked处理程序中:
public partial class AlertLambdasPage : ContentPage
{
public AlertLambdasPage()
{
InitializeComponent();
}
void OnButtonClicked(object sender, EventArgs args)
{
Task<bool> task = DisplayAlert("Simple Alert", "Decide on an option",
"yes or ok", "no or cancel");
task.ContinueWith((Task<bool> taskResult) =>
{
Device.BeginInvokeOnMainThread(() =>
{
label.Text = String.Format("Alert {0} button was pressed",
taskResult.Result ? "OK" : "Cancel");
});
});
label.Text = "Alert is currently displayed";
}
}
除了回调方法没有名称之外,这个程序和前一个程序之间没有什么区别。 他们是匿名的。 但有时lambda函数有可能模糊程序流,这当然就是这种情况。 在调用ContinueWith方法之后和传递给ContinueWith的回调执行之前,Label的Text属性设置为文本“当前显示警报”,但该语句出现在方法的底部。
在不扭曲程序流程的情况下,应该有更好的方法来表示您想要发生的事情。 这种更好的方式称为等待。
等待的警报
AlertAwait程序与AlertCallbacks和AlertLambdas具有相同的XAML文件,但OnButtonClicked方法大大简化:
public partial class AlertAwaitPage : ContentPage
{
public AlertAwaitPage()
{
InitializeComponent();
}
async void OnButtonClicked(object sender, EventArgs args)
{
Task<bool> task = DisplayAlert("Simple Alert", "Decide on an option",
"yes or ok", "no or cancel");
label.Text = "Alert is currently displayed";
bool result = await task;
label.Text = String.Format("Alert {0} button was pressed",
result ? "OK" : "Cancel");
}
}
关键声明是这样的:
bool result = await task;
该任务变量是从DisplayAlert返回的Task 对象,但是await关键字似乎神奇地提取了没有任何回调或lambda的布尔结果。
你应该知道的第一件事就是await实际上并没有等待警报被解雇!相反,C#编译器已经对OnButtonClicked方法进行了大量的手术。该方法基本上已经变成了状态机。单击按钮时将执行部分方法,稍后将执行部分方法。当执行流程命中await关键字时,暂时跳过OnButtonClicked方法的剩余部分。 OnButtonClicked方法退出并将控制权返回给操作系统。从Button的角度来看,事件处理程序已经完成。
当用户关闭警报框时,OnButtonClicked方法的其余部分将从为结果变量赋值布尔值开始继续执行。在某些情况下,可以在幕后进行一些优化。例如,如果异步操作立即完成,则执行流程可以正常继续。
await运算符还有另一个奖励:请注意,没有使用Device.BeginInvokeOnMainThead。当用户解除警报时,OnButtonClicked方法会自动在用户界面线程中恢复执行,这意味着它可以访问Label。 (在某些情况下,出于性能原因,您可能希望继续在后台线程中运行。如果是这样,您可以使用Task的ConfigureAwait方法来执行此操作。您将在本章后面看到一个示例。)
await关键字实质上将异步代码转换为看似正常的顺序命令代码的东西。当然,在幕后,这个程序与之前的两个程序之间并没有太大区别。在所有这三种情况下,OnButtonClicked处理程序在显示警报时将控制权返回给操作系统,并在警报解除时恢复执行。
仅出于说明的目的,三个程序在调用DisplayAlert方法后立即显示一些文本。如果没有必要,那么可以将DisplayAlert调用与await运算符组合以完全删除显式的Task 变量:
bool result = await DisplayAlert("Simple Alert", "Decide on an option",
"yes or ok", "no or cancel");
这就是await常见于代码中的方式。 DisplayAlert返回Task ,但是await运算符在后台任务完成后有效地提取bool结果。
实际上,您可以像使用任何其他运算符一样使用await,它可以出现在更复杂的表达式中。 例如,如果您不需要在DisplayAlert调用之后显示文本的语句,您实际上可以将await运算符和DisplayAlert放在最终的String.Format调用中:
async void OnButtonClicked(object sender, EventArgs args)
{
label.Text = String.Format("Alert {0} button was pressed",
await DisplayAlert("Simple Alert", "Decide on an option",
"yes or ok", "no or cancel") ? "OK" : "Cancel");
}
这可能有点难以阅读,但将await运算符和DisplayAlert方法的组合视为bool并且该语句非常有意义。
您可能已经注意到OnButtonClicked方法标有async关键字。 您使用await的任何方法都必须标记为async。 但是,async关键字不会更改方法的签名。 OnButtonClicked仍然有资格作为Clicked事件的事件处理程序。
但并非每种方法都可以是异步方法。