有没有办法在F#中识别CLR事件实例?

Rei*_*aka 5 f#

当我在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中找到任何属性或字段,以便根据所有者的其他实例以及该所有者中的不同事件来识别它?

Rei*_*aka 2

不完全是问题的答案,但我想不出许多其他需要识别事件实例的场景,所以也许这已经足够好了:

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或 杂耍IDisposableHandler对象。