如果我反编译Test2构造函数:
public class Test2 : VarArgTest
{
public Test2() : base("foo", __arglist("one", 2))
{
}
}
public class VarArgTest
{
public VarArgTest(string test, __arglist)
{
}
}
我得到这个IL:
IL_0000: ldarg.0
IL_0001: ldstr "foo"
IL_0006: ldstr "one"
IL_000b: ldc.i4.2
IL_000c: call instance vararg void VarargsTest.VarArgTest::.ctor(string,
...,
string,
int32)
我正在尝试使用ILGenerator生成相同的IL流,但EmitCall仅使用MethodInfo而不是ConstructorInfo,并且唯一的Emit重载采用ConstructorInfo不支持传入其他参数类型。
答案 0 :(得分:2)
看起来这是不可能的;我怀疑它仅仅是MethodInfo
被用作输入类型而非MethodBase
的疏忽,因为拥有varargs .ctor
似乎是完全有效的。您可以尝试提交错误,但我怀疑这是一个低优先级的方案来支持,因为varargs方法不符合CLS。
答案 1 :(得分:2)
可悲的是,似乎@kvb是正确的。也就是说,你想要努力工作的方法“GetMethodToken”似乎确实采用了方法库。因此,如果您不反对表达式树,则可以创建自己的方法版本。这是我最好的猜测(减去参数验证)
我没有在每个场景中对它进行全面测试,现在你可以使用func作为调用vararg方法或vararg ctor的方法。
::
var ilgen = Expression.Parameter(typeof(ILGenerator));
var code = Expression.Parameter(typeof(OpCode));
var method = Expression.Parameter(typeof(MethodBase));
var opttypes = Expression.Parameter(typeof(Type[]));
var stackchange = Expression.Variable(typeof(int));
var tok = Expression.Variable(typeof(int));
var paramTypes= Expression.Variable(typeof(Type[]));
var expr = Expression.Lambda<Action<ILGenerator, OpCode, MethodBase, Type[]>>(Expression.Block(
new[]{stackchange,tok,paramTypes},
Expression.Assign(stackchange, Expression.Constant(0)),
Expression.Assign(tok,
Expression.Call(ilgen,
typeof(ILGenerator)
.GetMethod("GetMethodToken", BindingFlags.NonPublic | BindingFlags.Instance),
method,
opttypes,
Expression.Constant(false)
)
),
Expression.Call(ilgen, typeof(ILGenerator).GetMethod("EnsureCapacity", BindingFlags.Instance | BindingFlags.NonPublic), Expression.Constant(7)),
Expression.Call(ilgen, typeof(ILGenerator).GetMethod("InternalEmit",BindingFlags.Instance|BindingFlags.NonPublic), code),
Expression.IfThen(
Expression.AndAlso(
Expression.Not(
Expression.Property(method, "IsConstructor")
),
Expression.Equal(
Expression.Property(
Expression.Convert(method, typeof(MethodInfo)),
"ReturnType"
),
Expression.Constant(typeof(void))
)
),
Expression.PostIncrementAssign(stackchange)
),
Expression.Assign(paramTypes, Expression.Call(method,
typeof(MethodInfo)
.GetMethod("GetParameterTypes", BindingFlags.NonPublic | BindingFlags.Instance)
)
),
Expression.IfThen(
Expression.AndAlso(
Expression.AndAlso(
Expression.TypeIs(method, Type.GetType("System.Reflection.Emit.SymbolMethod")),
Expression.Property(method, "IsStatic")
),
Expression.Equal(
code,
Expression.Constant(OpCodes.Newobj, typeof(OpCode))
)
),
Expression.PostDecrementAssign(stackchange)
),
Expression.IfThen(Expression.NotEqual(opttypes, Expression.Constant(null)),
Expression.SubtractAssign(stackchange, Expression.ArrayLength(opttypes))
),
Expression.Call(ilgen, typeof(ILGenerator).GetMethod("UpdateStackSize", BindingFlags.NonPublic | BindingFlags.Instance), code, stackchange),
Expression.Call(ilgen, typeof(ILGenerator).GetMethod("RecordTokenFixup", BindingFlags.NonPublic | BindingFlags.Instance)),
Expression.Call(ilgen, typeof(ILGenerator).GetMethod("PutInteger4", BindingFlags.NonPublic | BindingFlags.Instance),tok)
),
ilgen, code, method, opttypes);
var func = expr.Compile();
从这里你可以像你这样在你的类型上使用它:
public class VarArgTest
{
public int CountOfArgs;
public VarArgTest(string test, __arglist)
{
ArgIterator args = new ArgIterator(__arglist);
CountOfArgs = args.GetRemainingCount();
}
}
//然后在方法中创建类
var ab = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Foo"), AssemblyBuilderAccess.RunAndSave);
var mb = ab.DefineDynamicModule(ab.GetName().Name, ab.GetName().Name + ".dll", true);
var tb = mb.DefineType("Foo", TypeAttributes.BeforeFieldInit | TypeAttributes.AnsiClass | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.Public, typeof(VarArgTest));
var ctor = tb.DefineConstructor(MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,CallingConventions.HasThis,Type.EmptyTypes);
var il =ctor.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldstr, "foo");
il.Emit(OpCodes.Ldstr, "one");
il.Emit(OpCodes.Ldc_I4_2);
func(il, OpCodes.Call, typeof(VarArgTest).GetConstructors()[0], new[] { typeof(string), typeof(int) });
il.Emit(OpCodes.Ret);
var v=Activator.CreateInstance(tb.CreateType());
Console.WriteLine((v as VarArgTest).CountOfArgs);
打印2。
答案 2 :(得分:2)
好的,看了这个post
我发现了一种非常简单的方法: 您可以使用方法构建器中的可选参数获取构造函数的标记。为什么这么令人难以置信的无证是一个谜。我之前的答案中的程序的类似版本在下面做同样的事情,但只使用这个get methodtoken api。 这更容易!
var ab = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Foo"), AssemblyBuilderAccess.RunAndSave);
var mb = ab.DefineDynamicModule(ab.GetName().Name, ab.GetName().Name + ".dll", true);
var tb = mb.DefineType("Foo", TypeAttributes.BeforeFieldInit | TypeAttributes.AnsiClass | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.Public, typeof(VarArgTest));
var ctor = tb.DefineConstructor(MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.HasThis, Type.EmptyTypes);
var il = ctor.GetILGenerator();
var token= mb.GetConstructorToken(typeof(VarArgTest).GetConstructors()[0], new[] { typeof(string), typeof(int) });
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldstr, "foo");
il.Emit(OpCodes.Ldstr, "one");
il.Emit(OpCodes.Ldc_I4_2);
il.Emit(OpCodes.Call,token.Token);
il.Emit(OpCodes.Ret);
var v = Activator.CreateInstance(tb.CreateType());
Console.WriteLine((v as VarArgTest).CountOfArgs);