22

在 F# 中发布事件的标准方式现在似乎如下:

type MyDelegate = delegate of obj * EventArgs -> unit

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

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

这很好用,包括能够从其他 .NET 语言(至少我测试过的 C#)中使用该事件。但是如何让该事件显示在界面中呢?这就是我正在尝试的...

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

但这不会编译 - 接口中的成员会导致错误:“未找到与此覆盖相对应的抽象或接口成员”。我应该如何在我的界面中声明事件?还是我的成员语法错误?

注意 - 这不是关于在 F# 中使用 C# 事件或在 F# 中使用一般事件 - 接口方面是关键。

4

2 回答 2

22

您需要在接口和实现上都指定 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
于 2012-11-14T14:38:35.300 回答
13

除了 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

原因是 F# 编译器可以通过两种方式编译事件:

  • 当您指定属性时,该事件将被编译为具有底层addremove操作的 .NET 事件(与 C# 兼容)。

  • 当您不指定属性时,该事件将编译为一个 .NET 属性(带有get),该属性返回一个类型的值IEvent<'T>(在 F# 核心库中定义)。

因此,您的接口定义必须与实现匹配(遗憾的是,自动推断这一点非常棘手,因此编译器不会这样做)。

您需要哪个选项?如果您要向 C# 公开代码,那么您肯定需要CLIEvent. 如果您只是从 F# 中使用它,那么它并不重要 - 但是,当您经常将事件传递给函数时避免使用它可能会更有效 - 例如在myTyp.OnMyEvent |> Event.map (...)
(在第一个表示中,事件需要被包裹到一个IEvent<_>对象中,而在第二种情况下,它可以使用属性获取对象)。

于 2012-11-14T21:39:02.340 回答