简体   繁体   中英

F# casting an object to an interface

I have a class called 'Pane' (think glass pane) that implements 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
            ...

I create a list of panes (Pane list):

let p = [ p1; p2 ]

however I need to cast this to an IPane list as this is a parameter type in another function. The following code produces an error:

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'

This is confusing as Pane implements IPane. Simply passing the Pane list object as a parameter into the required function also produces an error.

How do I cast Pane list to IPane list?

F# doesn't allow inheritence in quite the way you want.

A better way would be to use:

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

Alternatively, you can change your function to use something like this

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

and here you can do f [p1;p2] as the # tells the compiler that any type that inherits from IPane is fine.

Another way would have been like this:

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

In your original code, what happened is that first it inferred the type of [p1; p2] [p1; p2] without any constraints, which it found to be Pane list , and then it tried to upcast it to IPane list , which it couldn't do because F# has strict variance rules: even if Pane is a subtype of IPane , Pane list is not a subtype of IPane list .

In my code above, on the other hand, instead of trying to infer the type of [p1; p2] [p1; p2] , it verifies that it can match the type IPane list that it was given; and in doing so, it implicitly upcast the individual list elements p1 and p2 to IPane .

The solution was easier than I thought.

I was defining p1 and p2 as:

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

However if I cast them as IPanes at this point, everything works:

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

John Palmer's solution above is probably more rigorous as the original pane objects are maintained as the concrete types.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM