如何检查F#中是否正在处理事件

时间:2010-01-21 16:49:30

标签: f#

以下C#代码的F#等价物是什么?具体来说,我需要检查是否正在处理事件。

protected virtual void OnClicked(ClickEventArgs e) {
    if (this.Clicked != null) //how can I perform this check in F#
        this.Clicked(this, e);
}

5 个答案:

答案 0 :(得分:2)

好吧,我想我想出了这件事。从Don Syme's blog获取提示,特别是“IEvent模块的实现”部分。

而不是以下内容:

let validationFailedEvent = new Event<DataValidationEventHandler, DataValidationEventArgs>()

我必须自己实现IEvent并创建一个变量来保存调用列表:

let mutable listeners: Delegate = null

let validationFailedEvent = { new IEvent<DataValidationEventHandler, DataValidationEventArgs> with 
                                    member x.AddHandler(d) = 
                                        listeners <- Delegate.Combine(listeners, d)
                                    member x.RemoveHandler(d) =
                                        listeners <- Delegate.Remove(listeners, d)
                                    member x.Subscribe(observer) =
                                        let h = new Handler<_>(fun sender args -> observer.OnNext(args))
                                        (x :?> IEvent<_,_>).AddHandler(h)
                                        { new System.IDisposable with 
                                             member x.Dispose() = (x :?> IEvent<_,_>).RemoveHandler(h) } }

然后,检查是否有侦听器,如果没有,则引发异常:

member private x.fireValidationFailedEvent(e:DataValidationEventArgs) =
    match listeners with
    | null -> failwith "No listeners"
    | d -> d.DynamicInvoke([| box x; box e |])

答案 1 :(得分:2)

实现RequiresSubscriptionEvent的另一种方法是在现有Event功能(使用组合)的基础上构建,只需添加一个计数器,该计数器计算已注册处理程序的数量并添加属性{{1 (如果你想要的话,甚至可以发布监听器的数量......)

这使代码更容易使用,并且希望也更安全,因为如果你不检查它是否有任何listneres,它仍然可以像通常的F#代码一样工作。如果你想进行检查,你可以......

HasListeners

样本用法:

type RequiresSubscriptionEvent<_>() = 
  let evt = new Event<_>()
  let mutable counter = 0
  let published = 
    { new IEvent<_> with
      member x.AddHandler(h) = 
        evt.Publish.AddHandler(h)
        counter <- counter + 1; 
      member x.RemoveHandler(h) = 
        evt.Publish.RemoveHandler(h)
        counter <- counter - 1; 
      member x.Subscribe(s) = 
        let h = new Handler<_>(fun _ -> s.OnNext)
        x.AddHandler(h)
        { new System.IDisposable with 
            member y.Dispose() = x.RemoveHandler(h) } }
  member x.Trigger(v) = evt.Trigger(v)
  member x.Publish = published
  member x.HasListeners = counter > 0

尽管这不是标准F#库的一部分,但它显示了F#头等事件的巨大优势。如果缺少一些功能,您可以自己实现它!

答案 2 :(得分:1)

通常,您不需要在F#中进行检查(事件基础结构会为您检查):

type T() =
  let ev = new Event<_>()
  [<CLIEvent>]
  member x.Event = ev.Publish
  member x.OnClicked() =
    ev.Trigger()

答案 3 :(得分:1)

我遵循了kvb的建议并把这个逻辑放在了一个类中。我从F#源复制了Event并添加了Handled属性,该属性检查Delegate是否为null。我尝试添加,然后从事件中删除处理程序,以确保它被设置回null,实际上确实如此。

    type EventEx<'Delegate,'Args when 'Delegate : delegate<'Args,unit> and 'Delegate :> System.Delegate >() = 
        let mutable multicast : System.Delegate = null
        static let argTypes = 
            let instanceBindingFlags = BindingFlags.Instance ||| BindingFlags.Public ||| BindingFlags.NonPublic ||| BindingFlags.DeclaredOnly
            let mi = typeof<'Delegate>.GetMethod("Invoke",instanceBindingFlags)
            mi.GetParameters() |> (fun arr -> arr.[1..]) |> Array.map (fun p -> p.ParameterType)

        member x.Handled = (multicast <> null)

        member x.Trigger(sender:obj,args:'Args) = 
            match multicast with 
            | null -> ()
            | d -> 
                if argTypes.Length = 1 then 
                    d.DynamicInvoke([| sender; box args |]) |> ignore
                else
                    d.DynamicInvoke(Array.append [| sender |] (Microsoft.FSharp.Reflection.FSharpValue.GetTupleFields(box args))) |> ignore
        member x.Publish = 
            { new IEvent<'Delegate,'Args> with 
                member x.AddHandler(d) =
                    multicast <- System.Delegate.Combine(multicast, d)
                member x.RemoveHandler(d) = 
                    multicast <- System.Delegate.Remove(multicast, d) 
                member e.Subscribe(observer) = 
                   let h = new Handler<_>(fun sender args -> observer.OnNext(args))
                   (e :?> IEvent<_,_>).AddHandler(h)
                   { new System.IDisposable with 
                        member x.Dispose() = (e :?> IEvent<_,_>).RemoveHandler(h) } }

答案 4 :(得分:0)

这篇文章http://geekswithblogs.net/Erik/archive/2008/05/22/122302.aspx说你不需要检查F#中的空事件,但我不知道他的引用是什么。

Don Symes撰写的这篇文章http://blogs.msdn.com/dsyme/articles/FSharpCompositionalEvents.aspx非常详细地介绍了F#事件。看起来事件不归F#

中的类所有

从上面,

  

事件现在是一流的   F#langauge中的值。确实,   事件不是一个单独的概念   所有在语言设计中,相反,   事件只是类型的值   Microsoft.FSharp.Idioms.IEvent&lt; _&gt;,和   .NET事件实际上是公正的   这种类型的属性。

并且

  

C#的一个限制是   事件只能作为成员存在   在课堂上。使用F#模型,   可以创建新的事件值   作为任何表达的一部分的值。