2009-05-20 10 views
0

F#と.Netを学ぶために、リリースされる予定のDLRを試してみました。デリゲート/ DLR Lambdaを使用してインスタンスメソッドをオーバーライドするには?

この目的のために、私はclrとうまく統合された基本的なタイプのシステムを実装しようと努力しています。 Objectを継承するシンプルな型をインスタンス化できますが、定義したメソッドを呼び出すときにエラーが発生します。

DLR LambdaExpressionsがデリゲートにコンパイルするため、生成されたデリゲートからMethodInfoを生成し、それを呼び出して、生成されたメソッドの引数をスタックに取り込みます。その後、それを返します。それは私のエラーを取得するこの時点でです。ここで

は私のコードです:

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)|]) 

ここに私のエラーが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 

すべてのヘルプや提案が私のミスので、ネットの初心者くさいのビットを - 私はには理解されるであろうシンプルにすることができます。

マイクコホート

+0

暗闇の中で撮影 - fsc.exeでコンパイルすると同じ動作になるのですか? MethodAccessExceptionは、何かがパブリックではないかもしれないと示唆していますが、すべてのコードがパブリックメソッドを生成すると思われるので、なぜそれが起こっているのかわかりません... – Brian

+0

Brianの提案に感謝します。 FSC/VS2008でこれをコンパイルした後、私はこの(VS2008のデバッグを経由して「例外の詳細」ペイン)を取得しています: System.Reflection.TargetInvocationExceptionは、未処理の メッセージだった:呼び出しのターゲットが例外をスローされました。 –

+0

私は、デリゲートオブジェクト自体がスタックにないという問題があると信じています....検証には時間がかかるでしょうが、それは私の問題かもしれません。 –

答えて

0

さて、私はそれを解決しました。生成された文章は悪かった。また、デリゲートについては、直面していた機能ではなく、ダイナミックに呼びかけなければなりませんでした。

#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)|]) 
関連する問題