当我在F#Interactive工作时,我经常想要对事件处理程序进行更改.简单地在事件上调用Subscribe或Add或AddHandler函数会导致旧事件继续被调用,这很少是意图.
一种解决方案是使用它返回的IDisposable,但这需要在您自己的代码中跟踪IDisposable,这对于探索性任务来说很麻烦.
当我Dictionary<IEvent,IDisposable>再次订阅同一个事件时,我试过调用Dispose():
let events = Dictionary<obj, IDisposable>()
let subonce (e:IEvent<'h,'e>) (handler: 'e -> unit) =
if events.ContainsKey e then
events.[e].Dispose()
events.Remove e |> ignore
let d = e.Subscribe handler
events.Add (e,d) |> ignore
let w = Window()
w.Show()
//Running this line in FSI a second time onward should Dispose() the previous subscription
subonce w.MouseUp (fun e -> printfn "%A" <| e.GetPosition(w))
Run Code Online (Sandbox Code Playgroud)
不幸的是,事实证明,F#生成了一个新的IEvent实例,因此天真地使用=或obj.Equals不切割它.
> w.MouseUp;;
val it : IEvent<Input.MouseButtonEventHandler,Input.MouseButtonEventArgs> =
<published event> {addHandler = <fun:it@5-70>;
createHandler = <fun:it@5-72>;
removeHandler = <fun:it@5-71>;}
> w.MouseUp;;
val it : IEvent<Input.MouseButtonEventHandler,Input.MouseButtonEventArgs> =
<published event> {addHandler = <fun:it@6-74>; //note that these functions are of a different anonymous instance
createHandler = <fun:it@6-76>;
removeHandler = <fun:it@6-75>;}
Run Code Online (Sandbox Code Playgroud)
我是否可以在IEvent中找到任何属性或字段,以便根据所有者的其他实例以及该所有者中的不同事件来识别它?
不完全是问题的答案,但我想不出许多其他需要识别事件实例的场景,所以也许这已经足够好了:
type OneHandler<'e> = { mutable h : 'e -> unit }
let onehandler (e:IEvent<'h,'e>) =
let h = { h = fun _ -> () }
e.Subscribe(fun e -> h.h e) |> ignore
h
let w = Window()
let wmouseup = onehandler w.MouseUp
wmouseup.h <- (fun e -> printfn "%A" <| e.GetPosition(w))
Run Code Online (Sandbox Code Playgroud)
这样,通过仅评估对 的赋值wmouseup.h,我们可以更改事件处理程序,而无需重新启动w或 杂耍IDisposable或Handler对象。