C# 如何使用ILGenerator使用VarArgs生成对基构造函数的调用

C# 如何使用ILGenerator使用VarArgs生成对基构造函数的调用,c#,il,ilgenerator,C#,Il,Ilgenerator,如果我反编译Test2构造函数: public class Test2 : VarArgTest { public Test2() : base("foo", __arglist("one", 2)) { } } public class VarArgTest { public VarArgTest(string test, __arglist) { } } 我得到这个消息: IL_0000: ldarg.0 IL_0001: ldst

如果我反编译Test2构造函数:

public class Test2 : VarArgTest
{
    public Test2() : base("foo", __arglist("one", 2))
    {

    }
}

public class VarArgTest
{
    public VarArgTest(string test, __arglist)
    {

    }
}
我得到这个消息:

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流,但只接受MethodInfo而不是ConstructorInfo,并且唯一接受ConstructorInfo的重载不支持传入其他参数类型。

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

遗憾的是@kvb似乎是正确的。这就是说,您想要进行艰苦工作的方法“GetMethodToken”似乎需要一个methodbase。因此,如果您不反对表达式树,您可以创建自己的方法版本。以下是我的最佳猜测(减去参数验证) 我还没有在每个场景中对其进行全面测试,但现在可以使用func作为方法来调用vararg方法或vararg-ctor。
::

//然后在方法中创建类

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。

好的,读了这篇文章之后

我发现了一种非常简单的方法: 您可以从方法生成器中获取具有可选参数的构造函数标记。为什么这是如此难以置信的未记录是一个谜。下面是我前面回答中的一个类似版本的程序,它只使用这个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);

@WilliamAndrewMontgomery代码编译得很好,但是Intellisense似乎不喜欢它(可能是因为您提到的原因)。@WilliamAndrewMontgomery是的,作为一个C语言构造,它是没有文档记录的,但我不是建议从C使用它,这只是为了说明生成IL的代码。据我所知,ILGenerator不是特定于语言的,它支持varargs进行方法调用(请参阅),因此问题是它是否也支持varargs进行基本构造函数调用。我认为
EmitCall
的文档说明
opcode
可以
Newobj
支持这一点。(
Newobj
仅对
ConstructorInfo
有意义,但对
MethodInfo
没有意义,对吗?)
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);
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);