推断类型的奇怪反射错误

rob*_*kuz 5 reflection f# system.reflection

尝试通过反射运行f#代码时,我有一些奇怪的效果.
给出以下类型

type Box<'a, 'b> = Box of 'a * 'b
Run Code Online (Sandbox Code Playgroud)

和这个功能

//iToS :: Box<'a,'b> -> Box<string,'b>
let iToS (Box (i, v)) = Box ((sprintf "%A" i), v)
Run Code Online (Sandbox Code Playgroud)

我可以轻松,正确地运行以下代码

let r01 = iToS (Box (1, 1))
Run Code Online (Sandbox Code Playgroud)

但是我需要在系统边界的尖锐边缘运行此功能,这样做的唯一方法是减少反射使用.
所以我创建了这个函数,它应该采用类似上面的函数和给定类型的记录并应用它.

let convert<'t> (f:Quotations.Expr) (v:'a) : 't =
    let methi e =
        let rec methi' e =
            match e with
                | Call (x, mi, y) -> mi
                | Lambda (_, body) -> methi' body
                | _ -> failwith <| sprintf "not a function %A" e
        methi' e

    let apply f v =
        let m = methi f
        m.Invoke(null, [|box v|])

    apply f v :?> 't
Run Code Online (Sandbox Code Playgroud)

如果我现在运行这个,如下所示.

let r10 = (convert<Box<string, int>> <@ iToS @>) (Box (1, 1))
Run Code Online (Sandbox Code Playgroud)

我收到以下错误

System.ArgumentException : Object of type 'Box`2[System.Int32,System.Int32]' cannot be converted to type 'Box`2[System.Object,System.Object]'.
at System.RuntimeType.CheckValue (System.Object value, System.Reflection.Binder binder, System.Globalization.CultureInfo culture, System.Reflection.BindingFlags invokeAttr) [0x0007d] in <8cd55ece525b4760b63de40980e005aa>:0
at System.Reflection.MonoMethod.ConvertValues (System.Reflection.Binder binder, System.Object[] args, System.Reflection.ParameterInfo[] pinfo, System.Globalization.CultureInfo culture, System.Reflection.BindingFlags invokeAttr) [0x0007f] in <8cd55ece525b4760b63de40980e005aa>:0
at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00014] in <8cd55ece525b4760b63de40980e005aa>:0
at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) [0x00000] in <8cd55ece525b4760b63de40980e005aa>:0
at convert[t] (Microsoft.FSharp.Quotations.FSharpExpr f, Box`2[a,b] v) [0x00006] in <5831a15618eafa12a745038356a13158>:0
at test convert () [0x000e6] in <5831a15618eafa12a745038356a13158>:0
at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&)
at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00038] in <8cd55ece525b4760b63de40980e005aa>:0
Run Code Online (Sandbox Code Playgroud)

谁试图将某些东西变成一个Box<obj, obj>,为什么?
任何帮助表示赞赏

PS:一些澄清;-)
a)这显然是关于在F#
b)的背景下使用反射的问题是的,我知道我的真正问题可以在没有反思的情况下解决,我已经这样做了.它可以轻松地将我的代码大小增加40%.
c)是的,我知道反思是狗慢.我愿意交换速度(我不需要)以获得更清晰的代码.

gso*_*mix 6

函数的签名convert包含显式泛型参数't,但不包含'a.它打破了参数的类型推断v.

应该:

let convert<'t, 'a> (f:Quotations.Expr) (v:'a) : 't
Run Code Online (Sandbox Code Playgroud)

但是明确的参数很难使用.我宁愿在表达式中存储有关转换的类型信息:

let convert (f:Quotations.Expr<'a -> 't>) (v:'a) : 't 
Run Code Online (Sandbox Code Playgroud)

示例(http://ideone.com/peLJAR):

let r10 = (convert <@ iToS @>) (Box (1, 1))
> val r10 : Box<string,int> = Box ("1",1)

let r20 = (convert <@ iToS @>) (Box (1.0, 1.0))
> val r20 : Box<string,float> = Box ("1.0",1.0)
Run Code Online (Sandbox Code Playgroud)

  • 我认为答案中的类型顺序错误.你能尝试`让我们转换(f:Quotations.Expr <'a - >'t>)(v:'a):'t =`? (2认同)