简体   繁体   English

如何在F#界面中声明事件?

[英]How should an event be declared in an F# interface?

The standard way to publish events in F# now seems to be the following: 现在,在F#中发布事件的标准方法如下:

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). 并且工作正常,包括能够从其他.NET语言中消耗该事件(C#至少我已经测试过)。 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. 注意 - 这不是关于消耗F#中的C#事件或F#中的一般事件使用 - 接口方面是关键。

You need to specify the CLIEvent attribute on both the interface and the implementation. 您需要在接口和实现上指定CLIEvent属性。

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: 除了thr的答案之外,编写在接口和实现中都不使用 CLIEvent属性的代码也是有效的:

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: 原因是F#编译器可以通过两种方式编译事件:

  • When you do specify the attribute, the event gets compiled into a .NET event (compatible with C#) with underlying add and remove operations. 当您指定属性时,事件将被编译为带有基础addremove操作的.NET事件(与C#兼容)。

  • 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). 如果未指定该属性,则会将事件编译为.NET属性(带有get ),该属性返回IEvent<'T>类型的值(在F#核心库中定义)。

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 . 如果您将代码暴露给C#,那么您肯定需要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 (...) 如果你只是从F#中使用它,那么它并不重要 - 但是,当你经常将事件传递给函数时,避免使用它可能更有效 - 例如在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). (在第一种表示中,事件需要包装到IEvent<_>对象中,而在第二种情况下,它可以使用属性获取对象)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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