15

我正在尝试复制一些IObservableButton.Click事件创建的 C# 代码。我想将此代码移植到 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);

如您所见,它确实采用objectandRoutedEventArgs作为参数,因此 F# 编译器实际上是正确的。

C# 编译器是否有一些魔法在幕后完成了 F# 编译器没有的工作,或者我只是在这里遗漏了一些东西?

无论哪种方式,我怎样才能在 F# 中完成这项工作?

4

2 回答 2

17

我知道制作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>

这是可能的,因为标准 .NET 事件的 F# 表示是类型(在这种情况下)IEvent<Windows.RoutedEventHandler,Windows.RoutedEventArgs>。从文档中可以看出,IEvent 实现了 IObservable。换句话说,在 F# 中,每个事件都已经是IObservable.

于 2011-02-27T06:25:29.807 回答
6

乔尔·穆勒(Joel Mueller)是现场的,所以只是为了记录: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)
)
于 2011-02-27T14:31:31.823 回答