F#将对象转换为接口

时间:2014-06-05 08:44:30

标签: interface f#

我有一个名为' Pane' (想想玻璃窗格)实现IPane:

type IPane =
    abstract PaneNumber : int with get, set
    abstract Thickness : float<m> with get, set
    abstract ComponentNumber : int with get, set
    abstract Spectra : IGlassDataValues with get, set
    ...

type Pane(paneNumber, componentNumber, spectra, ...) =
    let mutable p = paneNumber
    let mutable n = componentNumber
    let mutable s = spectra
    ...

    interface IPane with
        member this.PaneNumber
            with get() = p
            and set(value) = p <- value
        member this.ComponentNumber
            with get() = n
            and set(value) = n <- value
        member this.Spectra
            with get() = s
            and set(value) = s <- value
            ...

我创建了一个窗格列表(窗格列表):

let p = [ p1; p2 ]

但是我需要将它转换为IPane列表,因为这是另一个函数中的参数类型。以下代码产生错误:

let p = [ p1; p2 ] :> IPane list
'Type constraint mismatch. The type
  Pane list
is not compatible with type
  IPane list
The type 'IPane' does not match the type 'Pane'

当Pane实施IPane时,这很令人困惑。简单地将Pane list对象作为参数传递给所需的函数也会产生错误。

如何将窗格列表转换为IPane列表?

3 个答案:

答案 0 :(得分:8)

F#不会以你想要的方式允许继承。

更好的方法是使用:

[p1 ;p2] |> List.map (fun x -> x:> IPane)

或者,您可以将功能更改为使用此类

let f (t:#IPane list) = ()

在这里你可以做f [p1;p2]因为#告诉编译器任何继承自IPane的类型都没问题。

答案 1 :(得分:3)

另一种方式是这样的:

let p = [p1; p2] : IPane list
// or equivalently
let p : IPane list = [p1; p2]

在原始代码中,发生的事情是,首先它推断出[p1; p2]的类型没有任何约束,它发现是Pane list,然后它尝试将其向上转换为IPane list },它不能做,因为F#有严格的方差规则:即使PaneIPane的子类型,Pane list也不是IPane list的子类型。

在上面的代码中,另一方面,它不是试图推断[p1; p2]的类型,而是验证它是否可以匹配给出的类型IPane list;在这样做时,它隐含地将单个列表元素p1p2向上翻转为IPane

答案 2 :(得分:1)

解决方案比我想象的要容易。

我将p1和p2定义为:

let p1 = new Pane(1, 2, blah, blah)
let p2 = new Pane(2, 2, blah, blah)

但是,如果我此时将它们作为IPanes投射,那么一切正常:

let p1 = new Pane(1, 2, blah, blah) :> IPane
let p2 = new Pane(2, 2, blah, blah) :> IPane

上面的John Palmer的解决方案可能更加严谨,因为原始窗格对象被维护为具体类型。