How to check if an event is being processed in F #

What is the F # equivalent of the following C # code? In particular, I need to check if the event is being processed.

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

Ok, I think I figured it out. Take a replica from the Don Syme blog , in particular the section "Implementing the IEvent Module".

Instead of the following:

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

I had to implement IEvent myself and create a variable to hold the call list:

 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) } } 

Then, to check if there are listeners, and if not, throw an exception:

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

An alternative way to implement RequiresSubscriptionEvent is to build on top of the existing Event functionality (using composition) and just add a counter that counts the number of registered handlers and adds the HasListeners property (or even publishes the number of listeners if you want ...)

This makes the code a little easier to use and also hopes to be more secure, because if you don't check if it has any lists, it will still work like regular F # code. And if you want to check, you can ...

 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 

Sample Usage:

 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!" 

Although this is not part of the standard F # libraries, it shows the great advantage of F # first-class events. If there is any missing functionality, you can simply implement it yourself!

+2
source

As a rule, you do not need to do this check in F # (the event framework checks you):

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

I followed the kvb suggestion and put this logic in the class. I copied the event from F # sources and added a Handled property that checks if the delegate is null. I tried adding and then removing handlers from the event to make sure that it is returning to a zero value, and that is true.

  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) } } 
+1
source

This article here http://geekswithblogs.net/Erik/archive/2008/05/22/122302.aspx says that you do not need to check for null events in F #, although I donโ€™t know what its link is.

This article by Don Symes http://blogs.msdn.com/dsyme/articles/FSharpCompositionalEvents.aspx goes into detail in F # events. It seems that events do not belong to a class in F #

From the foregoing,

this is what events are now top-notch values โ€‹โ€‹in F # langauge. In fact, events are not a separate concept with everything in terms of language; rather, events are simply values โ€‹โ€‹like Microsoft.FSharp.Idioms.IEvent <_> and .NET Events are just properties of this type.

AND

One of the limitations of C # is that events can only exist as members within classes. With the F # model, new event values โ€‹โ€‹can only be created as values โ€‹โ€‹as part of any expression.

0
source

Source: https://habr.com/ru/post/1299021/


All Articles