F# propper 删除 ElapsedEventHandler 处理程序
F# propper remove of ElapsedEventHandler handler
嗨,我有一个问题,我有一个快速程序,每 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
问题是我无法按原样正确删除处理程序,如果我在它启动后按回车键并每 10 秒给我一条消息,那么这就是我的目标。但是,如果我按回车键 3 次,它会递增并给我 3 条消息,依此类推,但我只想要一条。
另一件事是,如果我从中删除参数,它会完美运行,所以我想问题出在参数上。对此有什么解决方案吗?
您当前实施的问题是每次调用 OnTimedEvent
returns 都是 ElapsedEventHandler
的新实例。当你这样称呼它时:
timer.Elapsed.RemoveHandler(OnTimedEvent frameId)
您正在删除以前未注册的新处理程序,因此实际上没有任何反应。当您将代码更改为 add/remove 相同的处理程序时,您将始终使用相同的实例:
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)
此代码重构了您必须存储处理程序的内容,以便可以将其删除。
open System
open System.Timers
[<EntryPoint>]
let main argv =
let timer = new Timer(float 10000, AutoReset = true)
let onTimedEvent (frameId: uint32) : ElapsedEventHandler = new ElapsedEventHandler (fun obj args -> printfn "DATE: %A FRAME: %i" DateTime.Now frameId)
let rec readKey frameId =
let handler = onTimedEvent frameId
timer.Elapsed.AddHandler(handler)
timer.Start()
let keyStroke = Console.ReadKey()
timer.Stop()
timer.Elapsed.RemoveHandler(handler)
printfn "%c pressed" keyStroke.KeyChar
let nextFrameId =
if keyStroke.Key.Equals(ConsoleKey.Enter) then
frameId + 1u
else
frameId
readKey(nextFrameId)
readKey(1u)
0
可能有更好的方法来完成您所追求的,但这回答了您的问题。
嗨,我有一个问题,我有一个快速程序,每 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
问题是我无法按原样正确删除处理程序,如果我在它启动后按回车键并每 10 秒给我一条消息,那么这就是我的目标。但是,如果我按回车键 3 次,它会递增并给我 3 条消息,依此类推,但我只想要一条。
另一件事是,如果我从中删除参数,它会完美运行,所以我想问题出在参数上。对此有什么解决方案吗?
您当前实施的问题是每次调用 OnTimedEvent
returns 都是 ElapsedEventHandler
的新实例。当你这样称呼它时:
timer.Elapsed.RemoveHandler(OnTimedEvent frameId)
您正在删除以前未注册的新处理程序,因此实际上没有任何反应。当您将代码更改为 add/remove 相同的处理程序时,您将始终使用相同的实例:
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)
此代码重构了您必须存储处理程序的内容,以便可以将其删除。
open System
open System.Timers
[<EntryPoint>]
let main argv =
let timer = new Timer(float 10000, AutoReset = true)
let onTimedEvent (frameId: uint32) : ElapsedEventHandler = new ElapsedEventHandler (fun obj args -> printfn "DATE: %A FRAME: %i" DateTime.Now frameId)
let rec readKey frameId =
let handler = onTimedEvent frameId
timer.Elapsed.AddHandler(handler)
timer.Start()
let keyStroke = Console.ReadKey()
timer.Stop()
timer.Elapsed.RemoveHandler(handler)
printfn "%c pressed" keyStroke.KeyChar
let nextFrameId =
if keyStroke.Key.Equals(ConsoleKey.Enter) then
frameId + 1u
else
frameId
readKey(nextFrameId)
readKey(1u)
0
可能有更好的方法来完成您所追求的,但这回答了您的问题。