2016-11-20 6 views
5

Ich habe einige seltsame Effekte, wenn ich f # -Code über Reflektion ausführen möchte.
folgenden TypsMerkwürdiger Reflexionsfehler beim abgeleiteten Typ

type Box<'a, 'b> = Box of 'a * 'b 

und diese Funktion

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

ich leicht und korrekt den folgenden Code ausführen kann

let r01 = iToS (Box (1, 1)) 

jedoch diese Funktion an den scharfen Kanten Ich muss laufen meiner Systemgrenzen und der einzige Weg dies zu tun ist, auf die Reflektion zurückzugreifen.
Also habe ich diese Funktion erstellt, die eine Funktion wie oben und eine Aufzeichnung des angegebenen Typs nehmen und anwenden soll.

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 

Wenn ich das jetzt aber wie unten ausführe.

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

bekomme ich folgende Fehler

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 

Wer etwas in eine Box<obj, obj> und warum zu konvertieren versucht? Einige Erläuterungen ;-)
a) dies ist ausdrücklich eine Frage über die Verwendung von Reflexion im Rahmen von F #
b) ja, ich weiß, dass mein eigentliches Problem gelöst werden könnte, ohne Reflexion:
Jede Hilfe

PS geschätzt und ich habe es schon getan. Es erhöht meine Code-Größe um 40% leicht.
c) Ja, ich weiß, Reflexion ist Hund langsam. Ich bin bereit, Geschwindigkeit (ich brauche nicht) für saubereren Code zu handeln.

Antwort

6

Eine Signatur der Funktion convert enthält den expliziten generischen Parameter 't, aber nicht 'a. Es bricht Typinferenz für das Argument v.

Sollte sein:

let convert<'t, 'a> (f:Quotations.Expr) (v:'a) : 't 

Aber explizite Parameter schwer zu bedienen sind. Ich würde eher eine Art Informationen zur Konvertierung im Ausdruck speichern:

let convert (f:Quotations.Expr<'a -> 't>) (v:'a) : 't 

Beispiele (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) 
+0

Hallo, danke für die Antwort tun - leider funktioniert es nicht :-( 'lassen konvertieren (f: Notierungen.Expr <'t -> 'a>) (v:' a): 't' wird nicht einmal kompilieren, während 'lassenkonvertieren(f: Quotations.Expr) (v: 'a):' t 'erhält den gleichen Fehler wie zuvor. Zusätzlich ist das größte Problem, dass ich die Typen nicht als generische Parameter setzen kann, wie ich es dann nicht habe (zumindest nicht in diesem Teil des Programms) – robkuz

+2

Ich denke, dass Typen in der falschen Reihenfolge in der Antwort sind. Könnten Sie versuchen, lassen Sie konvertieren (f: Zitate.Expr <'a -> 't>) (v:' a): 't = '? –

+0

Danke! Ich werde diesen Tippfehler beheben. – gsomix

3

Wer versucht, etwas in eine Box zu konvertieren und warum?

Da Sie Reflexion verwenden, Ihre MethodInfo liest die allgemeinen Parameter wie obj hier

Call (x, mi, y) -> mi 

Dann könnten Sie eine upcast wie unten

let r10 = (convert<Box<string, obj>> <@ iToS @>) (Box((upcast 1 : obj), (upcast 1 : obj))) 
Verwandte Themen