可以为从接口继承的类型创建一个多态工厂吗?

Can one have a polymorphic factory for types inheriting from an interface?

根据 SO 和其他地方的建议,我尝试使用接口实现多态性,如下面的简化示例所示。

type IFace =
   // abstract methods
   abstract member foo: int -> int

type X(i: int) =
    // some code
    interface IFace with
        member this.foo j = i + j

type Y(s: string) =
    // some code
    interface IFace with
        member this.foo j = (int s) + j

type Z(x: float) =
    // some code
    interface IFace with
        member this.foo j = (int x) + j

X、Y 和 Z 的构造函数可以使用这些值:

let zX = 1
let zY = "2"
let zZ = 3.0

我想建立一个类似

的多态工厂
let create (str: string) =
    match str with
    | "X" -> X(zX)
    | "Y" -> Y(zY)
    | "Z" -> Z(zZ)
    | _ -> failwith "Something went wrong"

let XObj = create "X"    
let YObj = create "Y"
let ZObj = create "Z"

但是,create 将无法编译,因为它会 return 不同类型的对象。

一种替代方法是将 create 中的所有情况向上转换为 System.Object:

let create1 (str: string) =
    match str with
    | "X" -> X(zX) :> System.Object
    | "Y" -> Y(zY) :> System.Object
    | "Z" -> Z(zZ) :> System.Object
    | _ -> failwith "Something went wrong"

然后 create1 会编译,但它的 return 值(我相信)是无用的,除非向下转换为 X、Y 或 Z。但这又引发了多态性问题:一般向下转换我需要一个 returns 不同类型值的函数。

另一种选择是使用歧视联盟

type ClassDU =
    | XClass of X
    | YClass of Y
    | ZClass of Z

并使用上面定义的函数create。但这是行不通的。如果不向上转换,编译器仍然会将不同的情况视为具有不同的类型。由于 ClassDU 不是 class,因此无法向上转换为 ClassDU。

在此代码段中

http://fssnip.net/k6/title/-F-Factory-Pattern

使用抽象 class XYZ 解决了问题,X、Y 和 Z 将从中继承抽象方法 foo。类似于 create 的函数会将所有情况向上转换为抽象 class:

let create2 (str: string) =
    match str with
    | "X" -> X(zX) :> XYZ
    | "Y" -> Y(zY) :> XYZ
    | "Z" -> Z(zZ) :> XYZ
    | _ -> failwith "Something went wrong"

是否可以为从接口继承的类型创建多态工厂,或者是否必须像代码片段中那样创建抽象 class?

接口

要对接口执行此操作,您只需向上转换为接口类型,例如:

let create (str: string) =
    match str with
    | "X" -> X(zX) :> IFace
    | "Y" -> Y(zY) :> IFace
    | "Z" -> Z(zZ) :> IFace
    | _ -> failwith "Something went wrong"

这有类型签名:

val create : str:string -> IFace

受歧视的工会

要对 DU 执行此操作,您需要在 create 函数中添加适当的 case 构造函数。

type ClassDU =
    | XClass of X
    | YClass of Y
    | ZClass of Z

let createDU (str: string) =
    match str with
    | "X" -> XClass <| X(zX) 
    | "Y" -> YClass <| Y(zY)
    | "Z" -> ZClass <| Z(zZ)
    | _ -> failwith "Something went wrong"

这有类型签名:

val createDU : str:string -> ClassDU

我认为您对受歧视工会行为的误解源于这样一个事实,即您认为它们就像是抽象的 类 具有多个 sub类,将它们看作具有多个构造函数.

的单一类型更好更准确