Как преобразовать событие WPF Button.Click в Observable, используя Rx и F#

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

Вот оригинальный код C#, который компилируется без ошибок:

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 конструктор.

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

Интересно, что в обоих примерах кода (C# и F#) тип h является EventHandler<RoutedEventArgs>,

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

Ошибка 2 Предполагалось, что это выражение имеет тип obj -> RoutedEventArgs -> unit, но здесь имеет тип EventHandler

Подпись для RoutedEventHandler что я нашел внутри PresentationCore:

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

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

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

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

2 ответа

Решение

Самый простой способ, которым я знаю, чтобы сделать 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,

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

Observable.FromEvent(
    (fun h -> RoutedEventHandler(fun sender e -> h.Invoke(sender, e))),
    (fun h -> b.Click.AddHandler h),
    (fun h -> b.Click.RemoveHandler h)
)
Другие вопросы по тегам