在 IlGenerator 中调用其他方法
Call other method in IlGenerator
我正在通过 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
}
我要生成的class是这样的:
public class GeneratedClass : ApiController
{
public MyInterface myImplementation { get; set; }
public MyResponse Hello1()
{
return myImplementation.Hello1();
}
// ... rest of implementation, it doesn't matter
}
如您所见,我想从其他对象调用方法。我知道如何为对象创建 属性 但我不知道如何从其他对象调用方法
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
{
// ...
}
}
如您所见,如果 OpCode
是 Call
、Callvirt
或 Newobj
,Emit()
会调用 EmitCall()
,因此 Emit()
vs 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);
我正在通过 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
}
我要生成的class是这样的:
public class GeneratedClass : ApiController
{
public MyInterface myImplementation { get; set; }
public MyResponse Hello1()
{
return myImplementation.Hello1();
}
// ... rest of implementation, it doesn't matter
}
如您所见,我想从其他对象调用方法。我知道如何为对象创建 属性 但我不知道如何从其他对象调用方法
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
{
// ...
}
}
如您所见,如果 OpCode
是 Call
、Callvirt
或 Newobj
,Emit()
会调用 EmitCall()
,因此 Emit()
vs 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);