简体   繁体   中英

How should an event be declared in an F# interface?

The standard way to publish events in F# now seems to be the following:

type MyDelegate = delegate of obj * EventArgs -> unit

type MyType () =
    let myEvent = new Event<MyDelegate, EventArgs> ()

    [<CLIEvent>]
    member this.OnMyEvent = myEvent.Publish

and that works fine, including being able to consume that event from other .NET languages (C# at least that I've tested). But how can you make that event show up in an interface? Here's what I'm trying...

type MyDelegate = delegate of obj * EventArgs -> unit

type IMyType =
    abstract member OnMyEvent : IEvent<MyDelegate, EventArgs>

type MyType () =
    let myEvent = new Event<MyDelegate, EventArgs> ()

    interface IMyType with
        [<CLIEvent>]
        member this.OnMyEvent = myEvent.Publish

But that won't compile - the member within the interface causes an error: "No abstract or interface member was found that corresponds to this override". How should I be declaring the event in my interface? Or is it that my member syntax is wrong?

Note - this is not about consuming C# events in F# or general event usage in F# - the interface aspect is key.

You need to specify the CLIEvent attribute on both the interface and the implementation.

open System;

type MyDelegate = delegate of obj * EventArgs -> unit

type IMyType =
    [<CLIEvent>]
    abstract member OnMyEvent : IEvent<MyDelegate, EventArgs>

type MyType () =
    let myEvent = new Event<MyDelegate, EventArgs> ()

    interface IMyType with
        [<CLIEvent>]
        member this.OnMyEvent = myEvent.Publish

In addition to the thr's answer, it is also valid to write code that does not use the CLIEvent attribute in both the interface and the implementation:

type MyDelegate = delegate of obj * EventArgs -> unit

type IMyType =
    abstract member OnMyEvent : IEvent<MyDelegate, EventArgs>

type MyType () =
    let myEvent = new Event<MyDelegate, EventArgs> ()    
    interface IMyType with
        member this.OnMyEvent = myEvent.Publish

The reason for this is that the F# compiler can compile events in two ways:

  • When you do specify the attribute, the event gets compiled into a .NET event (compatible with C#) with underlying add and remove operations.

  • When you don't specify the attribute, the event is compiled as a .NET property (with get ) that returns a value of a type IEvent<'T> (defined in the F# core library).

So, your interface definition has to match with the implementation (sadly, infering this automatically is quite tricky, so the compiler does not do that).

Which option do you need? If you're exposing code to C#, then you definitely need CLIEvent . If you're using it just from F# then it does not matter too much - however, it might be more efficient to avoid using it when you often pass the event to functions - for example in myTyp.OnMyEvent |> Event.map (...)
(in the first representation, the event needs to be wrapped into an IEvent<_> object while in the second case, it can just obtain the object using the property).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM