【问题标题】:Call other method in IlGenerator在 IlGenerator 中调用其他方法
【发布时间】:2016-06-17 00:07:36
【问题描述】:

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

问题是我不知道如何使用ILGenerator.EmitILGenerator.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
}

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

【问题讨论】:

  • 带有 OpCode OpCodes.Ldstr 的指令需要一个字符串作为参数,而不是一个字节。
  • 你事先知道方法的参数类型吗?以完全动态的方式生成呼叫并不适合胆小的人。您必须发出正确的(类型安全的)代码来加载每个参数。
  • 我得到了MethodInfo 的存储位置。我什么都不知道
  • 通过调用DynamicAssembly.Save(...)将生成的程序集写入文件,然后使用peverify.exe分析您的方法无效的原因。
  • 如果您不能通过这种方式解决它,您可以创建一个可编译的示例来重现您的问题的情况,并且可以对其进行全面分析。

标签: c# reflection reflection.emit


【解决方案1】:

来源:(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
    {
        // ...
    }
}

如您所见,如果OpCodeCallCallvirtNewobjEmit() 调用EmitCall(),所以Emit()EmitCall() 应该没有区别。

使用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);

【讨论】:

  • 还是一样。顺便说一句,我现在正在使用不带任何参数的方法进行测试(只是返回)。
  • 如果没有参数,新方法将只有两条指令:调用(其他方法)Ret
  • 正常情况下可以有参数,现在我只是测试没有。第二件事,我应该使用Emit 还是EmitCall
  • 查看修改后的答案 - 不应该有所作为。
猜你喜欢
  • 2018-01-02
  • 1970-01-01
  • 2017-06-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多