首页 > 解决方案 > F# propper 删除 ElapsedEventHandler 处理程序

问题描述

嗨,我有一个问题,我有一个快速程序,它会在每 10 秒内执行一次操作,它看起来很简单:

[<EntryPoint>]
let main argv =

    let timer = new Timer(float 10000)
    let OnTimedEvent (frameId:uint32) : ElapsedEventHandler = new ElapsedEventHandler (fun obj args -> printfn "DATE: %A FRAME: %i" DateTime.Now frameId)

    while(true) do
        let keyStroke = Console.ReadKey() 
        if keyStroke.Key.Equals(ConsoleKey.Enter) then
            let frameId = 1u
            timer.AutoReset <- true
            timer.Elapsed.RemoveHandler(OnTimedEvent frameId)
            timer.Elapsed.AddHandler(OnTimedEvent frameId)
            timer.Start();
        else
            printfn "%c pressed" keyStroke.KeyChar
    0 

问题是我无法按原样正确删除处理程序,如果我在启动后按 Enter 并每 10 秒给我一条消息,那么这就是我的目标。但是,如果我按 Enter 3 次,它会增加并给我 3 个消息,依此类推,但我只想要一个。

另一件事是,如果我从中删除参数,它会完美运行,所以我认为问题出在参数上。有什么解决方案吗?

标签: f#f#-interactive

解决方案


您当前实现的问题是每次调用都会OnTimedEvent返回ElapsedEventHandler. 当您按如下方式调用它时:

timer.Elapsed.RemoveHandler(OnTimedEvent frameId)

您正在删除以前未注册的新处理程序,因此实际上没有任何反应。当您更改代码以添加/删除相同的处理程序时,您始终使用相同的实例:

let frameId = 1u
let timedHandler = new ElapsedEventHandler (fun obj args -> 
    printfn "DATE: %A FRAME: %i" DateTime.Now frameId)

timer.Elapsed.RemoveHandler(timedHandler)
timer.Elapsed.AddHandler(timedHandler)

现在您没有一种将 传递frameId给您的事件处理程序的好方法。在您的代码中,frameId总是1u如此,因此很难看到您真正想要什么,但您可以使其可变:

let mutable frameId = 1u
let timedHandler = new ElapsedEventHandler (fun obj args -> 
    printfn "DATE: %A FRAME: %i" DateTime.Now frameId)

frameId <- 2u
timer.Elapsed.RemoveHandler(timedHandler)
timer.Elapsed.AddHandler(timedHandler)

也就是说,目前还不清楚你想要做什么,也许有一种完全不同的方式来做你想做的事。

一种完全不同的方法是使用MailboxProcessor保持当前frameId并处理两种类型的消息 - 一种由计时器每 10 秒触发一次,另一种可用于更改帧 ID:

type Message = 
  | Tick 
  | ChangeFrameId of uint32 

let agent = MailboxProcessor.Start(fun inbox -> 
  let rec run frameId = async {
    let! msg = inbox.Receive()
    match msg with
    | ChangeFrameId newId -> 
        return! run newId
    | Tick ->
        printfn "DATE: %A FRAME: %i" DateTime.Now frameId
        return! run frameId }
  run 1u)

let timer = new Timer(float 10000, AutoReset = true)
timer.Elapsed.Add(fun _ -> agent.Post(Tick))
timer.Start()

agent.Post(ChangeFrameId 2u)

推荐阅读