Подтвердить что ты не робот

Как должно быть объявлено событие в интерфейсе F #?

Стандартный способ публикации событий в F # теперь выглядит следующим образом:

type MyDelegate = delegate of obj * EventArgs -> unit

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

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

и это прекрасно работает, включая возможность использовать это событие на других языках .NET(С# по крайней мере, что я тестировал). Но как вы можете сделать это событие в интерфейсе? Вот что я пытаюсь...

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 # или об использовании общего события в F # - аспект интерфейса является ключевым.

4b9b3361

Ответ 1

Вам необходимо указать атрибут 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

Ответ 2

В дополнение к основному ответу также допустимо писать код, который не использует атрибут 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 # может скомпилировать события двумя способами:

  • Когда вы укажете атрибут, событие будет скомпилировано в событие .NET(совместимое с С#) с базовыми операциями add и remove.

  • Если атрибут не указан, событие компилируется как свойство .NET(с get), которое возвращает значение типа IEvent<'T> (определенное в основной библиотеке F #).

Итак, ваше определение интерфейса должно соответствовать реализации (к сожалению, сделать это автоматически довольно сложно, поэтому компилятор этого не делает).

Какой вариант вам нужен? Если вы показываете код на С#, вам определенно нужно CLIEvent. Если вы используете его только с F #, это не имеет большого значения - однако, возможно, более эффективно избегать его использования, когда вы часто передаете событие в функции - например, в myTyp.OnMyEvent |> Event.map (...)
(в первом представлении событие должно быть обернуто в объект IEvent<_>, а во втором случае оно может просто получить объект, используя свойство).