3

如果我反编译 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 并且唯一采用 ConstructorInfo 的Emit重载不支持传入其他参数类型。

4

3 回答 3

2

看起来这是不可能的;我怀疑这只是一个MethodInfo被用作输入类型而不是 的疏忽MethodBase,因为拥有 varargs 似乎是完全有效.ctor的。您可以尝试提交错误,但我怀疑这是一个需要支持的低优先级方案,因为可变参数方法不符合 CLS。

于 2014-05-23T13:47:33.620 回答
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。

于 2014-06-03T17:48:06.100 回答
2

好的,看完这篇文章

我发现了一种非常简单的方法:您可以从方法构建器中获取带有可选参数的构造函数的标记。为什么这是如此令人难以置信的无证记录是一个谜。下面是我之前的答案中程序的一个类似版本,它做同样的事情,但只使用这个 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);
于 2014-06-04T15:12:44.023 回答