【问题标题】:F# ICastableTo<'T> equivalent?F# ICastableTo<'T> 等效?
【发布时间】:2014-01-25 05:30:20
【问题描述】:

您不能在 F# 中实现通用接口的多个实例。就我而言,这很糟糕,因为我计划实现一个名为 ICastableTo 的接口,该接口可用于模式匹配:

type ICastableTo<'T> =
    /// <summary>
    /// Returns a casted version of the object
    /// </summary>
    member this.Value : 'T

...
(*x:obj*)
match x with
| :? ICastableTo<SomeType> as x -> doSomethingWith(x.Value)
| _ -> invalidOp("can't cast like that")

但是,当我尝试实际使用它时,我遇到了一个问题,因为我无法实现 ICastableTo 接口的多个版本(请参阅Implementing the same interface at different generic instantiations),但我的一些类实际上可以转换为多个类型。

我在这里最好的选择是什么?我当然可以定义一个 ICastable 接口并使用一个“PossibleCasts”属性,该属性将公开所有可用的强制转换委托,但这不是超级漂亮,并且不能很好地继承。


我的代码如下所示:

type BindableFunction<'T,'V>(func : 'T -> 'V) =
    member val Parent : 'T = nothing with get, set
    interface Specializable with
        member SpecializeFor(x: obj) =
            match x with | :? ICastable<'T> as x -> Parent <- x.Value | _ -> invalidOp("")

然后我就有了我的可转换类。例如,我有一个 TCell 类,它引用了一个 Cell(组合),因此可以将其转换为一个 Cell 用于函数绑定,即使这两种类型之间没有继承链接。

我想我最终要做的是将“与 | ICastable 匹配的 x”生成为非泛型(即,我将使用 ICastableOf_Cell 并使用反射找到好的接口(按名称获取类型) 然后使用 Reflection.Emit 生成代码。

另一种选择是为 BindableFunction 提供两种泛型类型,一种是值类型,另一种是 ICastableOf_Cell,这可能是一个更好的主意,但会使我的代码在各个地方更加冗长。

【问题讨论】:

  • 一个简单的问题,为什么你的x 参数和object 在这里,而不是例如泛型类型?
  • 参数x 并不是真正的object,但它的类型不能转换为SomeType(即使在运行时也是如此)。换句话说,它是一个继承自x 类型的类,它定义了到SomeType 的转换,而不是直接x 的类型。使用“匹配:?”模式正确,然后由于另一个 F# 限制,您必须将对象装箱,但这与此处无关。
  • implementing a type converter从你的类型中分离转换代码怎么样?
  • @Daniel 是的,这是个好主意,实际上,如果没有更好的解决方案,我最终可能会做什么。目前,我有一个类似的想法,涉及使用 Reflection.Emit 在运行时根据当前实现 ICastableTo<...> 的类型的已知属性动态构建类型转换器,但实际上创建通用转换函数并非不可行并在那里工作。这仍然没有我想的那么优雅,但是这越来越接近了,谢谢。
  • @FremyCompany:静态类型系统中的类型转换本质上是不优雅。如果您可以提供有关您的用例的更多详细信息,并且可能提供一些类型定义,那么其他解决方案可能会变得显而易见。

标签: generics interface casting f#


【解决方案1】:

最后,我选择了以下方法:

type BindableFunction<'T,'V>(convert: obj -> 'T, func : 'T -> 'V) =
    member val Parent : 'T = nothing with get, set
    interface Specializable with
        member SpecializeFor(x: obj) =
            match x with 
            | :? 'T as x -> (Parent <- x) 
            | _          -> (Parent <- convert(x))

这允许我为每种类型使用唯一的接口,因此不使用通用接口。

let CastToCell = ref(fun(o:obj) -> match o with | :? ICellScope as o -> o.ICell.Cell | _ -> invalidOp("invalid scope conversion"))

最后:

new BindableFunction<Cell,_>(!CastToCell,...)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-29
    • 2014-02-28
    • 2013-02-18
    • 2011-02-11
    • 2016-05-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多