以下C#代码的F#等价物是什么?具体来说,我需要检查是否正在处理事件。
protected virtual void OnClicked(ClickEventArgs e) {
if (this.Clicked != null) //how can I perform this check in F#
this.Clicked(this, e);
}
答案 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#模型, 可以创建新的事件值 作为任何表达的一部分的值。