使用 IL Emit 调用现有对象的方法 [英] Calling a method of existing object using IL Emit

查看:19
本文介绍了使用 IL Emit 调用现有对象的方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试编写一个基于属性的拦截器(类似于DynamicProxy).这个想法是,基于某些自定义属性,将调用该属性内的方法,即

I am attempting to write an attribute based interceptor (something similar to DynamicProxy). The idea being, based on certain custom attributes, a method inside that attribute would be called, i.e.

  • 在调用实际方法之前调用属性类中的方法.
  • 调用实际方法.

我能够使用 MethodBuilderTypeBuilder 覆盖现有方法.但是,我不知道如何调用Attribute内部的方法.

I am able to override existing method using MethodBuilder and TypeBuilder. However, I can't figure out how to call the method inside the Attribute.

我的代码:

static void CreateMethods<T>(TypeBuilder tb)
        {
            foreach (var methodToOverride in typeof(T).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly))
            {
                var attribute = (ProxyMethod)methodToOverride.GetCustomAttribute(typeof(ProxyMethod));
                if (attribute == null)
                    continue;

                MethodBuilder methodBuilder = tb.DefineMethod(
                    methodToOverride.Name,
                    MethodAttributes.Public
                    | MethodAttributes.HideBySig
                    | MethodAttributes.NewSlot
                    | MethodAttributes.Virtual
                    | MethodAttributes.Final,
                    CallingConventions.HasThis,
                    methodToOverride.ReturnType,
                    Type.EmptyTypes
                );

                ILGenerator il = methodBuilder.GetILGenerator();

                il.Emit(OpCodes.Ldstr, "The I.M implementation of C"); //step1
                il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) })); //step1

                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Call, methodToOverride);
                il.Emit(OpCodes.Ret);

                tb.DefineMethodOverride(methodBuilder, methodToOverride);
            }
        }

我认为我应该做的是将 attribute 加载到堆栈上,然后通过发出对 MethodInfo 的调用来调用 attribute.attributeMethod().然而,在我所看到的任何地方,我都可以找到使用 OpCodes.NewObj 创建对象的新实例的示例.我不想用这个,因为属性可能有参数.

What I think I should do is load attribute onto stack, then call attribute.attributeMethod() by emitting a call to the MethodInfo. However, everywhere I look, I can find examples of creating a new instance of the objected using OpCodes.NewObj. I don't want to use this, because attributes may have parameters.

我想不出在属性类中调用方法(这将取代 step1 注释).

I can't think of any of calling the method inside attribute class (which would replace step1 comment).

根据评论,我试图移动 IL 中代码的 GetCustomAttribute 部分.这就是我现在拥有的

Based on comments, I'm trying to move the GetCustomAttribute part of the code in IL. This is what I have right now

il.Emit(OpCodes.Ldtoken, methodToOverride);
il.Emit(OpCodes.Ldtoken, typeof(ProxyMethod));
il.Emit(OpCodes.Call, typeof(Attribute).GetMethod("GetCustomAttribute", new [] { typeof(MemberInfo), typeof(Type) }));

它给我带来了错误.有什么提示吗?

It throws an error for me. Any tips?

推荐答案

动态代码生成总是有点烦人.让我们首先编写一些辅助方法来摆脱所有 .GetMethod 的东西:

Dynamic code generation is always kind of annoying. Let's cook up some helper methods first to get rid of all the .GetMethod stuff:

static class Method {
    public static MethodInfo Of<TResult>(Expression<Func<TResult>> f) => ((MethodCallExpression) f.Body).Method;
    public static MethodInfo Of<T>(Expression<Action<T>> f) => ((MethodCallExpression) f.Body).Method;
    public static MethodInfo Of(Expression<Action> f) => ((MethodCallExpression) f.Body).Method;
}

现在假设我们有 ProxyMethodAttribute 及其方法 MyMethod -- 以下是我们检索和调用它的方法:

Now let's say we have ProxyMethodAttribute and its method MyMethod -- here's how we'd retrieve that and call it:

il.Emit(OpCodes.Ldtoken, methodToOverride);
il.Emit(OpCodes.Call, Method.Of(() => MethodBase.GetMethodFromHandle(default(RuntimeMethodHandle))));
il.Emit(OpCodes.Ldtoken, typeof(ProxyMethodAttribute));
il.Emit(OpCodes.Call, Method.Of(() => Type.GetTypeFromHandle(default(RuntimeTypeHandle))));
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Call, Method.Of(() => Attribute.GetCustomAttribute(default(MemberInfo), default(Type), default(bool))));
il.Emit(OpCodes.Callvirt, Method.Of((ProxyMethodAttribute a) => a.MyMethod()));

注意我们如何需要调用 GetXXXFromHandle 以从生成的元数据标记生成实际实例,以及我们如何需要对 Attribute.GetCustomAttribute (MethodInfo.GetCustomAttribute 实际上并不存在,这是作为扩展方法实现的.)

Note how we need to call GetXXXFromHandle to produce actual instances from the metadata tokens generated, and how we need a more involved call to Attribute.GetCustomAttribute (MethodInfo.GetCustomAttribute doesn't actually exist, this is implemented as an extension method.)

这篇关于使用 IL Emit 调用现有对象的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆