我有一些可以正常工作的代码:
type App() =
inherit Application()
let stack = StackLayout(VerticalOptions = LayoutOptions.Center)
let label = Label(XAlign = TextAlignment.Center, Text = "Welcome to F# Xamarin.Forms!")
do
let tapRecognizer = new TapGestureRecognizer()
let handleTapEvent (sender:Object) (args:EventArgs) =
label.Text <- "Tapped at " + DateTime.Now.ToString()
()
let tapEventHandler = new EventHandler(handleTapEvent)
tapRecognizer.Tapped.AddHandler(tapEventHandler)
label.GestureRecognizers.Add(tapRecognizer)
但是,当我将参数从EventArgs更改为这样的派生类型时:
type TapEventArgs(someId:int) =
inherit EventArgs()
member this.SomeId = someId
let handleTapEvent (sender:Object) (args:TapEventArgs) =
label.Text <- args.SomeId.ToString() + " tapped"
()
调用AddHandler时出现以下错误
类型'EventArgs'与类型'TapEventArgs'不兼容
此外,如果我这样更改EventHandler:
let tapEventHandler = new EventHandler<TapEventArgs>(handleTapEvent)
我收到此错误
This expression was expected to have type 'EventHandler'
but here has type 'EventHandler<TapEventArgs>'
有什么方法可以强制这种派生类型?
答案 0 :(得分:2)
F#不会像C#那样自动插入向下转换(这是一件好事,不是bug)。您不能传递预期祖先类型的后代类型。
要呼叫filter
,您需要使用下线运算符foreach
手动插入下线,如下所示:
AddHandler
当已经知道目标类型时(例如您的情况),您可以在其下划线使用F#编译器从上下文中推断出它:
:>
答案 1 :(得分:1)
这里的问题是类型不同,这就是F#告诉您的。 tapRecognizer.Tapped
的类型为EventHandler
,因此调用它时,它的类型将为EventHandler
,即不是EventHandler<TapEventArgs>
,这是另一种类型。无法更改此方法,并且TapGestureRecognizer
的子类也无法实现,因为该类是密封的。
此外,您发布的代码将很难编译,因为它具有F#阻止的循环引用。 TapEventArgs
要求使用label
中定义的App
。 App
取决于TapEventArgs
。无法从TapEventArgs
更新UI,而是需要传入一个函数或暴露状态。
有一种使用Command
模式的解决方案,它允许将某些信息传递给回调并避免循环依赖。 TapGestureRecognizer
具有属性CommandParameter
,可以在其中设置值(obj类型)。可以通过Command
属性提供回调,该回调可以接收该值。这是完整的示例:
open Xamarin.Forms
open System
type App() as this =
inherit Application()
let stack = StackLayout(VerticalOptions = LayoutOptions.Center)
let label = Label(XAlign = TextAlignment.Center, Text = "Welcome to F# Xamarin.Forms!")
do
let tapRecognizer = new TapGestureRecognizer()
let handleTapEvent (x:obj) =
match x with
| :? int as someId -> label.Text <- someId.ToString() + " tapped"
| _ -> label.Text <- "Tapped at " + DateTime.Now.ToString()
tapRecognizer.Command <- new Command(Action<obj>(fun x -> handleTapEvent x))
label.GestureRecognizers.Add(tapRecognizer)
stack.Children.Add label
tapRecognizer.CommandParameter <- 42 // The value to be passed to the Command's callback
this.MainPage <- ContentPage(Content = stack)
请注意,由于Xamarin.Forms中定义类型的方式(使用obj
作为属性),因此必须使用强制转换。