2009-05-20 5 views
0

In dem Bemühen, F # und .Net zu lernen, habe ich mit dem zu veröffentlichenden DLR gespielt.Delegate/DLR Lambdas verwenden, um Instanzmethoden zu überschreiben?

Zu diesem Zweck habe ich mit Reflektion herumgespielt, um ein grundlegendes Typsystem zu implementieren, das sich gut in die clr integriert. Während ich in der Lage bin, einen einfachen Typ zu instanziieren, der Object erweitert, erhalte ich beim Aufruf der von ihm definierten Methode einen Fehler.

Da am Ende des Tages DLR LambdaExpressions zu Delegaten kompilieren, was ich tue, ist generierte MethodInfo aus dem generierten Delegaten und rufen Sie es auf, den Stapel mit den Argumenten der generierten Methode auffüllen. Dann gib es zurück. An diesem Punkt bekomme ich meinen Fehler.

Hier ist mein Code:

open System 
open System.Reflection 
open System.Reflection.Emit 

type ConstructorInformation= 
    {Types:System.Type array} 

type MethodInformation= 
    {ParamTypes:System.Type array; 
    Name:string 
    Impl:System.Delegate} 


let rec addConstructors (t:TypeBuilder) (baseType:System.Type) constructorInfos = 
    match constructorInfos with 
     |ci::rest -> 
      let cb = t.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard,ci.Types) 
      let ilGen = cb.GetILGenerator() 

      ilGen.Emit(OpCodes.Ldarg_0) 
      Array.iteri (fun (index:int) _-> ilGen.Emit(OpCodes.Ldarg, index+1)) ci.Types 
      ilGen.Emit(OpCodes.Call, baseType.GetConstructor(ci.Types)) 
      addConstructors t baseType rest 
     |[] ->() 

let rec addMethods (tb:TypeBuilder) baseType methodInfos = 
    match methodInfos with 
    |mi::rest -> 
     let mb = tb.DefineMethod(mi.Name, MethodAttributes.Public, typeof<obj>, mi.ParamTypes) 
     let ilGen = mb.GetILGenerator() 
     ilGen.Emit(OpCodes.Ldarg_0) 
     Array.iteri (fun index _ -> ilGen.Emit(OpCodes.Ldarg, index+1)) mi.ParamTypes 
     ilGen.EmitCall(OpCodes.Call, mi.Impl.Method, mi.ParamTypes) 
     ilGen.Emit(OpCodes.Ret) 
     addMethods tb baseType rest 
    |[] ->() 

let defineType (baseType:System.Type) constructorInfos methodInfos= 
    let ab = AppDomain.CurrentDomain.DefineDynamicAssembly(AssemblyName("test"), AssemblyBuilderAccess.Run) 
    let mb = ab.DefineDynamicModule("test") 
    let typeBuilder = mb.DefineType("testType", TypeAttributes.Public, baseType)// | TypeAttributes.Class 
    addConstructors typeBuilder baseType constructorInfos 
    addMethods typeBuilder baseType methodInfos 
    typeBuilder.CreateType() 

type Delegate1 = delegate of obj -> obj 
let echo y:#obj= (y :> obj) 
let del1 : Delegate1 = new Delegate1(echo) 

let mis:MethodInformation list=[{Impl=del1; Name="Echo"; ParamTypes=[|(typeof<obj>)|]}] 
let cis:ConstructorInformation list=[] 
let t= defineType (typeof<obj>) cis mis 
let cinfo = t.GetConstructor([||]) 
let instance =cinfo.Invoke([||]) 
instance.GetType() 
(t.GetMethod("Echo")).Invoke(instance, [| (1:>obj)|]) 

Hier ist mein Fehler, von fsi:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.MethodAccessException: [email protected](System.Object) 
    at testType.Echo(Object) 
    --- End of inner exception stack trace --- 
    at System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner) 
    at System.RuntimeMethodHandle.InvokeMethodFast(Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner) 
    at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks) 
    at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) 
    at <StartupCode$FSI_0002>.$FSI_0002._main() 
stopped due to error 

Jede Hilfe oder Anregungen geschätzt-ich bin so mein Fehler ein bisschen ein .Net newb würde kann einfach sein.

Mike Kohout

+0

Ein Schuss in die Dunkelheit - erhalten Sie das gleiche Verhalten beim Kompilieren mit fsc.exe? Die MethodAccessException schlägt vor, dass etwas nicht öffentlich ist, aber all dein Code scheint öffentliche Methoden zu generieren, also bin ich mir nicht sicher, warum das passiert ... – Brian

+0

Danke für den Vorschlag, Brian, aber es sieht so aus, als würde es immer noch passieren. Danach mit fsc/VS2008 kompilieren, erhalte ich diesen (über VS2008 Debugging-„Ausnahme Detail“ Bereich): System.Reflection.TargetInvocationException wurde nicht behandelt Nachricht: Exception durch das Ziel für einen Aufruf ausgelöst wurde. –

+0

Ich glaube, das Problem ist, dass das Delegat-Objekt selbst nicht auf dem Stapel ist .... es wird einige Zeit dauern, um zu überprüfen, aber ich denke, dass das mein Problem sein könnte. –

Antwort

0

Nun, ich habe es gelöst. Das generierte Cil war schlecht. Außerdem musste ich mich dynamisch auf den Delegaten konzentrieren, nicht auf die Funktion, der er gegenüberstand.

#light 

open System 
open System.Reflection 
open System.Reflection.Emit 

type ConstructorInformation= 
    {Types:System.Type array} 

type MethodInformation= 
    {ParamTypes:System.Type array; 
    Name:string; 
    Impl:System.Delegate; 
    mutable Field:FieldBuilder option} 


let rec addConstructors (t:TypeBuilder) (baseType:System.Type) constructorInfos = 
    match constructorInfos with 
     |ci::rest -> 
      let cb = t.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard,ci.Types) 
      let ilGen = cb.GetILGenerator() 

      ilGen.Emit(OpCodes.Ldarg_0) 
      Array.iteri (fun (index:int) _-> ilGen.Emit(OpCodes.Ldarg, index+1)) ci.Types 
      ilGen.Emit(OpCodes.Call, baseType.GetConstructor(ci.Types)) 
      addConstructors t baseType rest 
     |[] ->() 

let rec addMethods (tb:TypeBuilder) baseType methodInfos = 
    match methodInfos with 
    |mi::rest -> 
     let fb = tb.DefineField(mi.Name+"_field", typeof<Delegate>, FieldAttributes.Public); 
     mi.Field <- Some(fb) 
     let mb = tb.DefineMethod(mi.Name, MethodAttributes.Public, typeof<obj>, mi.ParamTypes) 
     let ilGen = mb.GetILGenerator() 
     let arrayLocal = ilGen.DeclareLocal((typeof<obj[]>)) 
     ilGen.Emit(OpCodes.Ldarg_0) 
     ilGen.Emit(OpCodes.Ldfld, fb) 
     ilGen.Emit(OpCodes.Ldc_I4, Array.length mi.ParamTypes) 
     ilGen.Emit(OpCodes.Newarr, typeof<obj>) 
     ilGen.Emit(OpCodes.Stloc, arrayLocal) 
     ilGen.Emit(OpCodes.Ldloc, arrayLocal) 
     Array.iteri (fun index _ -> ilGen.Emit(OpCodes.Ldc_I4, index) 
            ilGen.Emit(OpCodes.Ldarg, index+1) 
            ilGen.Emit(OpCodes.Stelem_Ref) 
            ilGen.Emit(OpCodes.Ldloc, arrayLocal)) mi.ParamTypes 
     ilGen.EmitCall(OpCodes.Callvirt, (mi.Impl.GetType()).GetMethod("DynamicInvoke", [|(typeof<obj[]>)|]), mi.ParamTypes) 
     ilGen.Emit(OpCodes.Ret) 
     addMethods tb baseType rest 
    |[] ->() 

let defineType (baseType:System.Type) constructorInfos methodInfos= 
    let ab = AppDomain.CurrentDomain.DefineDynamicAssembly(AssemblyName("test"), AssemblyBuilderAccess.Run) 
    let mb = ab.DefineDynamicModule("test") 
    let typeBuilder = mb.DefineType("testType", TypeAttributes.Public, baseType)// | TypeAttributes.Class 
    addConstructors typeBuilder baseType constructorInfos 
    addMethods typeBuilder baseType methodInfos 
    typeBuilder.CreateType() 

type Delegate1 = delegate of obj -> obj 
let echo y:#obj= (y :> obj) 
let del1 : Delegate1 = new Delegate1(echo) 

type Delegate2 = delegate of obj * obj -> obj 
let echoFirst (x:#obj) (y:#obj)=(x:>obj) 
let echoFirstDelegate:Delegate2 = new Delegate2(echoFirst) 
echoFirstDelegate.DynamicInvoke([|(1:>obj);(2:>obj)|]) 
//let mis:MethodInformation list=[{Impl=del1; Name="Echo"; ParamTypes=[|(typeof<obj>)|];Field=None}] 
//let cis:ConstructorInformation list=[] 
//let t= defineType (typeof<obj>) cis mis 
//let cinfo = t.GetConstructor([||]) 
//let instance =cinfo.Invoke([||]) 
//instance.GetType() 
//(t.GetField("Echo_field")).SetValue(instance, del1) 
//let fieldDelegate = (t.GetField("Echo_field")).GetValue(instance) :?> Delegate 
//(t.GetMethod("Echo")).Invoke(instance, [| (1:>obj)|]) 

//del1.DynamicInvoke([|(1:>obj)|]) 

let mis:MethodInformation list=[{Impl=del1; Name="Echo"; ParamTypes=[|(typeof<obj>)|];Field=None}; 
           {Impl=echoFirstDelegate; Name="EchoFirst"; ParamTypes=[| (typeof<obj>);(typeof<obj>)|]; Field=None}] 
let cis:ConstructorInformation list=[] 
let t= defineType (typeof<obj>) cis mis 
let cinfo = t.GetConstructor([||]) 
let instance =cinfo.Invoke([||]) 
instance.GetType() 
(t.GetField("Echo_field")).SetValue(instance, del1) 
let fieldDelegate = (t.GetField("Echo_field")).GetValue(instance) :?> Delegate 
(t.GetMethod("Echo")).Invoke(instance, [| (1:>obj)|]) 
(t.GetField("EchoFirst_field")).SetValue(instance, echoFirstDelegate) 
(t.GetMethod("EchoFirst")).Invoke(instance, [| (1:>obj);(2:>obj)|])