使用委托/DLR Lambda 覆盖实例方法?

发布于 2024-07-20 20:02:10 字数 3726 浏览 3 评论 0原文

为了学习 F# 和 .Net,我一直在尝试即将发布的 DLR。

为此,我一直在尝试反射,努力实现一个与 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

任何帮助或建议将不胜感激 - 我是一个 .Net 新手,所以我的错误可能很简单。

迈克·科胡特

In an effort to learn F# and .Net, I've been playing around with the to-be-released DLR.

To that end, I've been playing around with reflection, in an effort to implement a basic type system that integrates nicely with the clr. While I'm able to instantiate a simple type that extends Object, I get an error when calling the method it defines.

Because at the end of the day DLR LambdaExpressions compile down to delegates, what I do is take generated MethodInfo off the generated delegate and call it, populating the stack with the args of the generated method. Then return it. It's at this point I get my error.

Here's my 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)|])

Here's my error, from 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

Any help or suggestions would be appreciated-I'm a bit of a .Net newb so my mistake may be simple.

Mike Kohout

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

荭秂 2024-07-27 20:02:10

嗯,我解决了。 生成的 cil 很糟糕。 另外,我必须动态调用委托,而不是它所面对的函数。

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

Well, I solved it. The generated cil was bad. Also, I had to dynamicinvoke on the delegate, not the function it was fronting.

#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)|])
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文