gpt4 book ai didi

.net - WPF 的 F# 异步事件处理程序类似于 C# 的 async 和 await

转载 作者:行者123 更新时间:2023-12-03 12:19:26 25 4
gpt4 key购买 nike

如何在 F# 中编写异步 WPF(或 Windows 窗体)事件处理程序?具体来说,是否有任何类似于 C# 5 的 async 和 await 的编码模式?

这是一个完整的 C# WPF 应用程序:

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

class Program
{
static int IncrementSlowly(int previous)
{
Thread.Sleep(3000);
if (previous == 2) throw new Exception("Oops!");
return previous + 1;
}

static async void btn_Click(object sender, RoutedEventArgs e)
{
var btn = sender as Button;
btn.IsEnabled = false;
try
{
var prev = (int)btn.Content;
btn.Content = await Task.Run(() => IncrementSlowly(prev));
}
catch (Exception ex)
{
btn.Content = ex.Message;
}
finally
{
btn.IsEnabled = true;
}
}

[STAThread]
static void Main(string[] args)
{
var btn = new Button() { Content = 0 };
var win = new Window() { Content = btn };
btn.Click += btn_Click;
new Application().Run(win);
}
}

我无法弄清楚使用 F# 的等效项是什么。我使用异步工作流和异步方法的组合进行了多次尝试。它真的很快变得非常困惑。我希望有一种简单的方法可以让我忽略。

这是我的起点,它在 btn.Content <- incrementSlowly prev 锁定了 UI。 .我接下来该怎么做?
open System
open System.Threading
open System.Threading.Tasks
open System.Windows
open System.Windows.Controls

let incrementSlowly previous =
Thread.Sleep(3000)
if previous = 2 then failwith "Oops!"
previous + 1

let btn_Click (sender : obj) e =
let btn = sender :?> Button
btn.IsEnabled <- false
try
try
let prev = btn.Content :?> int
btn.Content <- incrementSlowly prev
with ex -> btn.Content <- ex.Message
finally
btn.IsEnabled <- true

[<EntryPoint>][<STAThread>]
let main _ =
let btn = new Button(Content = 0)
let win = new Window(Content = btn)
btn.Click.AddHandler(RoutedEventHandler(btn_Click))
Application().Run(win)

顺便说一下,假设 incrementSlowly无法修改。

最佳答案

第一步是制作incrementSlowly异步。这实际上在您的 C# 代码中是同步的,这可能不是一个好主意 - 在现实场景中,这可能是与网络通信,因此通常这实际上可能是异步的:

let incrementSlowly previous = async {
do! Async.Sleep(3000)
if previous = 2 then failwith "Oops!"
return previous + 1 }

现在,您可以使按钮单击处理程序也异步。我们将使用 Async.StartImmediate 启动它稍后确保我们可以访问 UI 元素,因此我们现在不必担心调度员或 UI 线程:
let btn_Click (sender : obj) e = async {
let btn = sender :?> Button
btn.IsEnabled <- false
try
try
let prev = btn.Content :?> int
let! next = incrementSlowly prev
btn.Content <- next
with ex -> btn.Content <- ex.Message
finally
btn.IsEnabled <- true }

最后一步是更改事件注册。像这样的事情应该可以解决问题:
btn.Click.Add(RoutedEventHandler(fun sender e ->
btn_Click sender e |> Async.StartImmediate)

关键是 Async.StartImmediate启动异步工作流。当我们在 UI 线程上调用它时,它确保所有实际工作都在 UI 线程上完成(除非您明确地将其卸载到后台),因此可以安全地访问代码中的 UI 元素。

关于.net - WPF 的 F# 异步事件处理程序类似于 C# 的 async 和 await,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23281990/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com