Как преобразовать событие WPF Button.Click в Observable с помощью Rx и F #

Я пытаюсь реплицировать некоторый код С#, который создает IObservable из события Button.Click. Я хочу передать этот код в F #.

Вот исходный код С#, который компилируется без ошибок:

Observable.FromEvent<RoutedEventHandler, RoutedEventArgs>(
                                    h => new RoutedEventHandler(h),
                                    h => btn.Click += h,
                                    h => btn.Click -= h))

Вот моя неудачная попытка сделать то же самое в F #:

Observable.FromEvent<RoutedEventHandler, RoutedEventArgs>(
            Func<EventHandler<RoutedEventArgs>, RoutedEventHandler>(fun h -> RoutedEventHandler(h)),
            Action<RoutedEventHandler>(fun h -> btn.Click.AddHandler h),
            Action<RoutedEventHandler>(fun h -> btn.Click.RemoveHandler h))

Все радует, за исключением второй строки заявления.

Компилятор F # жалуется на fun h -> RoutedEventHandler(h), потому что он не хочет исключать h в качестве параметра конструктору RoutedEventHandler.

С другой стороны, у компилятора С# нет проблем с принятием h => new RoutedEventHandler(h)

Интересно, что в обоих образцах кода (С# и F #) тип h равен EventHandler<RoutedEventArgs>.

Сообщение об ошибке, которое я получаю от компилятора F #:

Ошибка 2 Это выражение должно было иметь тип obj → RoutedEventArgs → unit, но здесь есть тип EventHandler

Подписи для RoutedEventHandler, которые я нашел внутри PresentationCore, это:

public delegate void RoutedEventHandler(object sender, RoutedEventArgs e);

Как вы можете видеть, он принимает параметры object и RoutedEventArgs, поэтому компилятор F # на самом деле правильный.

Есть ли какая-то магия, которую компилятор С# делает за кулисами, чтобы сделать эту работу компилятором F # или я просто что-то пропустил здесь?

В любом случае, как я могу сделать эту работу в F #?

Ответы

Ответ 1

Самый простой способ, с помощью которого я могу сделать IObservable<_> из кнопки WPF Button.Click, - это сделать его:

open System
open System.Windows.Controls

let btn = new Button()
let obsClick = btn.Click :> IObservable<_>

Изучение obsClick...

val obsClick : IObservable<Windows.RoutedEventArgs>

Это возможно, потому что представление F # стандартных событий .NET - это тип (в данном случае) IEvent<Windows.RoutedEventHandler,Windows.RoutedEventArgs>. Как вы можете видеть из документации, IEvent реализует IObservable. Другими словами, в F # каждое отдельное событие уже есть IObservable.

Ответ 2

Джоэл Мюллер находится на месте, поэтому только для записи: прямой перевод кода на С# будет

Observable.FromEvent(
    (fun h -> RoutedEventHandler(fun sender e -> h.Invoke(sender, e))),
    (fun h -> b.Click.AddHandler h),
    (fun h -> b.Click.RemoveHandler h)
)