我正在编写一个需要后续类型构建的库。图书馆使用平台.Net core 2.0
我使用Reflection.Emit
生成某种类型的问题public class GeneratedA : A, IA
{
public void DoInterface(string arg0, bool arg1, int arg2, object arg3, List<float> arg4, params object[] otherArgs)
{
DoClass(arg0, arg1, arg2, arg3, arg4, otherArgs);
}
}
对于这些类型:
public interface IA
{
void DoInterface(string arg0, bool arg1, int arg2, object arg3, List<float> arg4, params object[] otherArgs);
}
public class A
{
public void DoClass(params object[] args)
{
}
}
示例IL代码:
class Program
{
public static class Generator
{
public static T Create<T>()
where T : class
{
AssemblyName aName = new AssemblyName("DynamicAssembly");
AssemblyBuilder ab = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(Guid.NewGuid().ToString()), AssemblyBuilderAccess.Run);
ModuleBuilder mb = ab.DefineDynamicModule(aName.Name);
var interfaceType = typeof(T);
var interfaceMethod = interfaceType.GetMethod("DoInterface");
var interfaceMethodArgs = interfaceMethod.GetParameters().Select(x => x.ParameterType).ToArray();
var classType = typeof(A);
var classMethod = classType.GetMethod("DoClass");
var returnType = typeof(void);
var baseType = typeof(object);
var baseConstructor = baseType.GetConstructor(BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance, null, Type.EmptyTypes, null);
TypeBuilder tb = mb.DefineType("GeneratedA", TypeAttributes.Public, baseType);
ConstructorBuilder ctor = tb.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, Type.EmptyTypes);
ILGenerator ctorIL = ctor.GetILGenerator();
ctorIL.Emit(OpCodes.Ldarg_0);
ctorIL.Emit(OpCodes.Call, baseConstructor);
ctorIL.Emit(OpCodes.Nop);
ctorIL.Emit(OpCodes.Nop);
ctorIL.Emit(OpCodes.Ret);
tb.AddInterfaceImplementation(interfaceType);
MethodBuilder mbIM = tb.DefineMethod(interfaceType.Name + "." + interfaceMethod.Name,
MethodAttributes.Private | MethodAttributes.HideBySig |
MethodAttributes.NewSlot | MethodAttributes.Virtual |
MethodAttributes.Final,
returnType,
interfaceMethodArgs);
ILGenerator genIM = mbIM.GetILGenerator();
// ToDo
genIM.Emit(OpCodes.Call, classMethod);
genIM.Emit(OpCodes.Ret);
tb.DefineMethodOverride(mbIM, interfaceMethod);
Type t = tb.CreateType();
return Activator.CreateInstance(t) as T;
}
}
static void Main(string[] args)
{
IA a;
a = new GeneratedA();
a.DoInterface("0", true, 2, 3, new List<float>() { 4 }, "5", 6);
a = Generator.Create<IA>();
a.DoInterface("0", true, 2, 3, new List<float>() { 4 }, "5", 6);
}
}
每当我尝试填写评论“ToDo”时,我都会收到错误“Common Language Runtime检测到无效程序”。
我请求帮助调用方法DoClass。
由于
答案 0 :(得分:5)
要调用DoClass
方法,您需要提供参数,只有Call classMethod
不起作用。
第一个论点当然是“这个”参考:
genIM.Emit(OpCodes.Ldarg_0);
第二个参数是对象数组。 params
是编译器功能,如果您自己构建代码 - 您必须将其视为params
不存在。我的意思是 - 而
DoClass();
在代码中编写代码时,是合法的 - 编译为:
DoClass(new object[0]);
所以当你发出这个调用时 - 你应该总是提供对象数组参数,你不能省略它。
将对象数组推送到堆栈:
// push array length (0, for example) to stack
genIM.Emit(OpCodes.Ldc_I4_0);
// push new array with length given by the above value (0)
genIM.Emit(OpCodes.Newarr, typeof(object));
此时您的代码将编译并运行正常。这类似于:
public class GeneratedA : A, IA
{
public void DoInterface(string arg0, bool arg1, int arg2, object arg3, List<float> arg4, params object[] otherArgs)
{
DoClass();
}
}
如果要传递DoInterface
的所有参数,则需要更多工作。我将提供几个例子。传递第一个参数(string arg0
):
genIM.Emit(OpCodes.Dup);
// push index to store next element at (0)
genIM.Emit(OpCodes.Ldc_I4_0);
// push first argument (arg0 of DoInterface) to stack
genIM.Emit(OpCodes.Ldarg_1);
// store element in array at given index (yourArguments[0] = arg0)
genIM.Emit(OpCodes.Stelem_Ref);
传递第二个参数:
genIM.Emit(OpCodes.Dup);
// push index to store next element at (1)
genIM.Emit(OpCodes.Ldc_I4_1);
// push arg2
genIM.Emit(OpCodes.Ldarg_2);
// box, because boolean is value type, and you store it in object array
genIM.Emit(OpCodes.Box, typeof(bool));
// store in array (yourArguments[1] = (object) arg2
genIM.Emit(OpCodes.Stelem_Ref);
等等。
当您将参数推送到数组中时,请不要忘记更改其长度以反映参数的数量:
// push array length - 6
genIM.Emit(OpCodes.Ldc_I4_6);
// push new array with length given by the above value (6)
genIM.Emit(OpCodes.Newarr, typeof(object));
另请注意,您可能需要更改:
TypeBuilder tb = mb.DefineType("GeneratedA", TypeAttributes.Public, baseType);
到
TypeBuilder tb = mb.DefineType("GeneratedA", TypeAttributes.Public, classType);
或者只需更改为baseType = typeof(A)
,因为您希望从A
继承您生成的课程,而不是object
。
使用前2个args发出调用的完整代码:
ILGenerator genIM = mbIM.GetILGenerator();
genIM.Emit(OpCodes.Ldarg_0);
genIM.Emit(OpCodes.Ldc_I4_2);
genIM.Emit(OpCodes.Newarr, typeof(object));
genIM.Emit(OpCodes.Dup);
genIM.Emit(OpCodes.Ldc_I4_0);
genIM.Emit(OpCodes.Ldarg_1);
genIM.Emit(OpCodes.Stelem_Ref);
genIM.Emit(OpCodes.Dup);
genIM.Emit(OpCodes.Ldc_I4_1);
genIM.Emit(OpCodes.Ldarg_2);
genIM.Emit(OpCodes.Box, typeof(bool));
genIM.Emit(OpCodes.Stelem_Ref);
genIM.Emit(OpCodes.Call, classMethod);
genIM.Emit(OpCodes.Ret);