Как должно быть объявлено событие в интерфейсе 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 # - аспект интерфейса является ключевым.
Ответы
Ответ 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<_>
, а во втором случае оно может просто получить объект, используя свойство).