4

我得到了一些形式的计算结果'a option when 'a :> IBaseType。有从 派生的类型树IBaseType,我真的不知道这个是什么特定类型的选项,但重要的是它是特定派生的选项,而不是基类型。所以我想将它向上转换IBaseType option以进一步处理它。由于 option 是泛型类型,因此无法直接进行强制转换(在 F# 中),我必须在 Option.map 内部进行强制转换。没什么复杂的,类型推断按预期工作...... 推断ok 中间转换选项也按预期解决...... 中间推理ok 直到功能完成。此时,由于某种原因,类型推断决定原始选项必须已经是 type IBaseType option推理不好

中间类型之前已经解决了,为什么决定重新分配的推断类型op?当然这会导致运行时异常。看起来像编译器错误,但主要规则是编译器没有错误。

所以最后听起来真的很愚蠢:我不知道如何简单地向上转换简单的选项。只是为了让图片更清晰:作为论据processResultIBaseType option这是麻烦功能的来源:

(fun (x: obj) -> 
    let op = x :?> _ option
    let upcastOp = op |> Option.map (fun y -> y :> IBaseType)
    upcastOp |> processResult)

任何想法如何处理这个?

4

3 回答 3

3

首先,您是如何生产盒装物品的?最简单的解决方案是一开始就IBaseType option装箱而不是装箱#IBaseType options。如果由于某种原因这不可行,那么您可能需要使用反射。问题是在这段代码中:

let op = x :?> _ option
let upcastOp = op |> Option.map (fun y -> y :> IBaseType)

编译器知道这op是 a 'a option when 'a :> IBaseTypefor some'a但没有任何东西可以让编译器弄清楚'a实际上是什么,因为这种类型没有反映在函数的最终输出中 - 编译器需要为 提交特定类型'a,并且最好猜它可以做的只是基本类型IBaseType。你需要做这样的事情:

type ResultProcessor =
    static member ProcessResult<'a when 'a :> IBaseType> (op:'a option) =
        let upcastOp = op |> Option.map (fun y -> y :> IBaseType)
        upcastOp |> processResult

fun (x:obj) ->
    let ty = x.GetType()  // must be option<something>
    let [| tyArg |] = ty.GetGenericArguments()
    typeof<ResultProcessor>.GetMethod("ProcessResult").MakeGenericMethod(tyArg).Invoke(null, [|x|])    
于 2012-11-13T18:46:37.817 回答
1

我不知道这里的类型op是如何推断的。但我很确定,如果你不能按照 kvb 的建议将类型更改xIBaseType option,你真的必须使用反射。

另一种基于反射的解决方案:

let f (x:obj) =
   match x with
   | null -> None  // x is None
   | _ -> match x.GetType().GetProperty("Value") with
          | null -> None  // x is not an option
          | prop ->  
               let v = prop.GetValue( x, null )
               Some (v :?> IBaseType)
于 2012-11-13T20:01:25.397 回答
1

我支持kvb的解决方案。现在,在我为类似代码所做的一些基准测试中,对于绝对性能,我发现避免动态(未知)方法调用是一个优势。不知何故,使泛型类型的新实例化更快。例如:

[<AbstractClass>]
type BaseResultProcessor() =
    abstract member ProcessResult : obj -> option<IBaseType>

[<Sealed>]
type ResultProcessor<'T when 'T :> IBaseType>() =
    inherit BaseResultProcessor()
    override this.ProcessResult(x: obj) =
        match x :?> option<'T> with
        | Some x -> Some (x :> IBaseType)
        | None -> None

module Example =
    let run (x: obj) =
        let ty = x.GetType()
        let tyArg = ty.GetGenericArguments().[0]
        let p =
            typedefof<ResultProcessor<_>>.MakeGenericType(tyArg)
            |> Activator.CreateInstance :?> BaseResultProcessor
        p.ProcessResult(x)

至于问题出在哪里,以下“直观”的推理在 .NET 中是无效的:

'T1 :> 'T2
--------------------------
option<'T1> :> option<'T2>

我想说这是典型的类型系统——一旦考虑到它们与整个类型系统的交互,看起来简单或直观的东西很难或不可能正确实现。

于 2012-11-14T13:23:38.347 回答