带有对象表达式的 F# 多个接口实现

Onu*_*mus 7 f#

根据文档,您可以使用对象表达式实现多个接口。但是如果你看到下面的代码:


// Define two interfaces
type IFirst =
  abstract F : unit -> unit
  abstract G : unit -> unit

type ISecond =
  abstract H : unit -> unit
  abstract J : unit -> unit

// This object expression implements both interfaces.
let implementer : IFirst =
    { new ISecond with
        member this.H() = ()
        member this.J() = ()
      interface IFirst with
        member this.F() = ()
        member this.G() = () }
Run Code Online (Sandbox Code Playgroud)

所以强制转换IFirst会导致编译器错误。为什么呢?

Ast*_*sti 8

F#执行隐式转换。

let绑定中进行类型注释时,类型必须严格匹配表达式。例如,

let value : obj = new System.Collections.Generic.List<int>()
Run Code Online (Sandbox Code Playgroud)

将无法编译,即使 aList很明显是一个对象。

当你写:

let implementer : IFirst = expr 
Run Code Online (Sandbox Code Playgroud)

的类型expr必须绝对是IFirst. 没有像 C# 那样的隐式转换。

对象表达式将其类型作为实现的抽象类型,因此:

{ new ISecond with ... }
Run Code Online (Sandbox Code Playgroud)

将被推断为具有ISecond. 将它与非隐式强制转换规则结合起来,就会出现编译错误。

因为IFirstISecond不相关,您可以(运行时)向下转换为IFirst

let firstImplementer = implementer :?> IFirst
Run Code Online (Sandbox Code Playgroud)

另一种选择是制作一个组合界面:

type IBoth = inherit IFirst inherit ISecond
Run Code Online (Sandbox Code Playgroud)

并做:

let implementer =
    {
        new IBoth with ...
Run Code Online (Sandbox Code Playgroud)

这样你就可以自由(静态)向上转换为IFirstISecond

let firstImplementer = implementer :> IFirst
let secndImplementer = implementer :> ISecond
Run Code Online (Sandbox Code Playgroud)