代码之家  ›  专栏  ›  技术社区  ›  MAGx2

在IlGenerator中调用其他方法

  •  0
  • MAGx2  · 技术社区  · 9 年前

    我正在通过构建自己的类型 TypeBuilder 我正在尝试添加此方法,它将调用 methodInfo 从不同的对象收集。

    问题是我不知道怎么用 ILGenerator.Emit ILGenerator.EmitCall .

    我试过用 il.EmitCall(OpCodes.Call, methodInfo, arguments) il.Emit(OpCodes.Call, methodInfo) 但他们两个都没用。我总是遇到这个错误:

    [InvalidProgramException: Common Language Runtime detected an invalid program.]
       MyImplementationController.Hello1() +0
    
    [TargetInvocationException: Exception has been thrown by the target of an invocation.]
       System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor) +0
       System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments) +192
       System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) +155
       System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) +19
    

    这是我的代码:

            foreach (var methodInfo in methodInfosFromSomewhere)
            {
                var arguments = methodInfo.GetParameters().Select(a => a.ParameterType).ToArray();
                MethodBuilder mb = tb.DefineMethod(
                    methodInfo.Name,
                    MethodAttributes.Final | MethodAttributes.Public,
                    CallingConventions.HasThis,
                    methodInfo.ReturnType,
                    arguments);
    
                // method 
                ILGenerator il = mb.GetILGenerator();
                int numParams = arguments.Length;
                for (byte x = 0; x < numParams; x++)
                {
                    //il.Emit(OpCodes.Ldarg_S, x);
                    il.Emit(OpCodes.Ldstr, x);
                }
                //il.EmitCall(OpCodes.Call, methodInfo, arguments);
                il.Emit(OpCodes.Call, methodInfo);
    
                il.Emit(OpCodes.Ret);
            }
    

    @编辑

    我终于知道(大概)问题出在哪里了!我打电话的时候 Emit.Call 我不想在这个对象中调用方法。我想从另一个对象调用方法。

    请看这个例子:

    // this is interface that we want to 'decorate'
    public interface IMyInterface
    {
        MyResponse Hello1();
        MyResponse Hello2(MyRequest request);
        MyResponse Hello3(MyRequest request);
    }
    public class MyImplementation : IMyInterface
    {
        public MyResponse Hello1()
        {
            return new MyResponse { Name = "empty" };
        }
        // ... rest of implementation, it doesn't matter
    }
    

    我要生成的类类似于:

    public class GeneratedClass : ApiController
    {
        public MyInterface myImplementation { get; set; }
        public MyResponse Hello1()
        {
            return myImplementation.Hello1();
        }
        // ... rest of implementation, it doesn't matter
    }
    

    如您所见,我想从其他对象调用方法。我知道如何为对象创建属性,但不知道如何从其他对象调用方法

    1 回复  |  直到 9 年前
        1
  •  1
  •   Mr Anderson    9 年前

    来源:( http://referencesource.microsoft.com/#mscorlib/system/reflection/emit/ilgenerator.cs,3e110f4a19d1c05e )

    public virtual void Emit(OpCode opcode, MethodInfo meth)
    {
        //...
        if (opcode.Equals(OpCodes.Call) || opcode.Equals(OpCodes.Callvirt) || opcode.Equals(OpCodes.Newobj))
        {
            EmitCall(opcode, meth, null);
        }
        else
        {
            // ...
        }
    }
    

    正如你所见, Emit() 电话 EmitCall() 如果 OpCode Call , Callvirt Newobj 所以 发射() 发射呼叫() 不应该有什么不同。

    使用发射 OpCodes.Ldstr 需要类型为的操作数 string 。您要做的是在发出 OpCodes.Call 指示

    而不是:

    for (byte x = 0; x < numParams; x++)
    {
        il.Emit(OpCodes.Ldstr, x);
    }
    

    试试这个:

    switch (numParams)
    {
        case 0:
            break;
        case 1:
            il.Emit(OpCodes.Ldarg_0);
            break;
        case 2:
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldarg_1);
            break;
        case 3:
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldarg_1);
            il.Emit(OpCodes.Ldarg_2);
            break;
        default:
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldarg_1);
            il.Emit(OpCodes.Ldarg_2);
            il.Emit(OpCodes.Ldarg_3);
            for (int i = 4; i < numParams; i++)
            {
                il.Emit(OpCodes.Ldarg, mb.GetParameters()[i]);
            }
            break;
    }
    

    问题更新后编辑: 必须定义属性 myImplementation 在你的新类型中。

    试试这个:

    // Create field to back your "myImplementation" property
    FieldBuilder newBackingField = tb.DefineField("backingField_myImplementation", typeof(MyInterface), System.Reflection.FieldAttributes.Private);
    // Create your "myImplementation" property
    PropertyBuilder newProp = tb.DefineProperty("myImplementation", System.Reflection.PropertyAttributes.None, typeof(MyInterface), Type.EmptyTypes);
    // Create get-method for your property
    MethodBuilder getter = tb.DefineMethod("get_myImplementation", System.Reflection.MethodAttributes.Private);
    ILGenerator getterILGen = getter.GetILGenerator();
    // Basic implementation (return backing field value)
    getterILGen.Emit(OpCodes.Ldarg_0);
    getterILGen.Emit(OpCodes.Ldfld, newBackingField);
    getterILGen.Emit(OpCodes.Ret);
    
    // Create set-method for your property
    MethodBuilder setter = tb.DefineMethod("set_myImplementation", System.Reflection.MethodAttributes.Private);
    setter.DefineParameter(1, System.Reflection.ParameterAttributes.None, "value");
    ILGenerator setterILGen = setter.GetILGenerator();
    // Basic implementation (set backing field)
    setterILGen.Emit(OpCodes.Ldarg_0);
    setterILGen.Emit(OpCodes.Ldarg_1);
    setterILGen.Emit(OpCodes.Stfld, newBackingField);
    setterILGen.Emit(OpCodes.Ret);
    
    // Hello1 Method
    MethodBuilder hello1 = tb.DefineMethod("Hello1", System.Reflection.MethodAttributes.Public);
    ILGenerator il = hello1.GetILGenerator();
    
    // Here, add code to load arguments, if any (as shown previously in answer)
    
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Call, getter);
    il.Emit(OpCodes.Callvirt, typeof(MyInterface).GetMethod("Hello1"));
    il.Emit(OpCodes.Ret);