F# 如何检查F中是否正在处理事件#
以下C代码的F等价物是什么?具体来说,我需要检查是否正在处理事件F# 如何检查F中是否正在处理事件#,f#,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); } 本文说您不需要检查F#中的null事件,尽管我不知道他的引用是什么 唐·赛姆斯(Don Symes)的这篇文章非常详细地介绍了
protected virtual void OnClicked(ClickEventArgs e) {
if (this.Clicked != null) //how can I perform this check in F#
this.Clicked(this, e);
}
本文说您不需要检查F#中的null事件,尽管我不知道他的引用是什么
唐·赛姆斯(Don Symes)的这篇文章非常详细地介绍了F#事件。看起来事件不属于F中的类#
综上所述,
是的,现在的活动是一流的
F#langauge中的值。的确
事件并不是一个单独的概念
所有这些都是语言设计,
事件只是类型的值
Microsoft.FSharp.Idioms.IEvent和
.NET事件实际上是公正的
此类型的属性
及
C#的限制之一是
事件只能作为成员存在
在课堂上。对于F#模型,
只需创建新的事件值即可
作为值作为任何表达式的一部分
通常,您不需要在F#(事件基础结构为您检查)中执行该检查:
类型T()=
设ev=新事件()
[]
成员x.Event=ev.Publish
成员x.OnClicked()=
ev.Trigger()
好吧,我想我把这件事弄明白了。以“IEvent模块的实现”一节为线索
而不是以下内容:
let validationFailedEvent = new Event<DataValidationEventHandler, DataValidationEventArgs>()
实现
requireSubscriptionEvent
的另一种方法是在现有的事件
功能的基础上构建(使用组合),只需添加一个计数器来计算已注册处理程序的数量,并添加一个属性HasListeners
(如果需要,甚至可以发布侦听器的数量…)
这使得代码更容易使用,也更安全,因为如果不检查它是否有列表,它仍将像通常的F#代码一样工作。如果你想进行检查,你可以
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
类型requireSubscriptionEvent()=
让evt=新事件()
设可变计数器=0
让发布=
{新事件与
成员x.AddHandler(h)=
evt.Publish.AddHandler(h)
计数器0
示例用法:
type Demo() =
let evt = new RequiresSubscriptionEvent<_>()
[<CLIEvent>]
member x.OnSomething = evt.Publish
member x.FooThatFiresSomething() =
if evt.HasListeners then
evt.Trigger("foo!")
else
printfn "No handlers!"
类型Demo()=
设evt=new RequiresSubscriptionEvent()
[]
成员x.OnSomething=evt.Publish
成员x.FoothatfiresMething()=
如果evt.HasListeners那么
触发器(“foo!”)
其他的
printfn“没有处理程序!”
尽管这不是标准F#库的一部分,但它显示了F#头等事件的巨大优势。如果缺少一些功能,您可以自己实现它!我遵循kvb的建议,将此逻辑放在一个类中。我从F#源复制了事件,并添加了一个处理属性,用于检查Delegat是否我尝试添加到,然后从事件中删除处理程序,以确保它被设置回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) } }
键入EventEx和'Delegate:>System.Delegate>()=
让可变多播:System.Delegate=null
静态let argTypes=
让instanceBindingFlags=BindingFlags.Instance | | | | BindingFlags.Public | | | | BindingFlags.declareOnly
设mi=typeof()
|d->
如果argTypes.Length=1,则
d、 DynamicInvoke([|发送方;框参数])|>忽略
其他的
d、 DynamicInvoke(Array.append[| sender |](Microsoft.FSharp.Reflection.FSharpValue.GetTupleFields(框参数))|>忽略
成员x.发布=
{新事件与
成员x.AddHandler(d)=
多播IEvent).AddHandler(h)
{new System.IDisposable with
成员x.Dispose()=(e:?>IEEvent.RemoveHandler(h)}
如果没有处理事件,我想抛出一个异常(类似于未设置XmlReaderSettings.ValidationEventHandler时XmlReader抛出异常的方式)。此代码将从C#调用。没错,您不需要检查是否附加了处理程序来触发事件。我需要检查其他原因。(见我在kvb帖子中的评论)呵呵……我在谷歌搜索“F夏普听众”它得出了关于音符F#和听众的结果。因此,如果我们弄明白了这一点,我们可以让音乐家知道如何检查是否有人在听。没有F#专家,但我读了这篇文章,看起来他在重复一组观察者。也许这可以给你一些想法。也可以让你清楚地看到e C#编译器确实会把很多事情都抹掉。关于C#的性感,你当然可以把这个逻辑封装在你自己的requireSubscriptionEvent+1中。我同意你关于空检查的看法,但是空检查只是如何找出一个事件是否在C#中处理。在F#中可以做得不同。也许是监听器s可以存储在一个列表(可以检查是否为空)或一个选项(检查是否为空)中。不确定发布F#功能请求的最佳位置,但我在hubbs.net上发布了一个请求,以在事件中公开多播代理成员(和相关类型)。这将使此检查更容易。下面是功能请求:不幸的是,如果需要的话,我不认为这将正确处理同一物理委托的重复添加/删除。我也不认为这将保持侦听器的准确计数,因为您不知道对添加/删除处理程序的调用是否更改了invocat离子列表。嗯,你是对的。我希望AddHandler/RemoveHandler在这些奇怪的情况下会抛出一个异常。如果你多次添加一个处理程序,效果会很好,但不幸的是,你永远不知道删除处理程序是否有作用(你可以删除未添加的处理程序,它不会抱怨)。。。
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
type Demo() =
let evt = new RequiresSubscriptionEvent<_>()
[<CLIEvent>]
member x.OnSomething = evt.Publish
member x.FooThatFiresSomething() =
if evt.HasListeners then
evt.Trigger("foo!")
else
printfn "No handlers!"
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) } }