Search code examples
c#ililgenerator

How to generate call to base constructor with VarArgs using ILGenerator


If I decompile the Test2 constructor:

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

    }
}

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

    }
}

I get this 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)

I'm trying to generate the same IL stream using the ILGenerator but EmitCall only takes a MethodInfo not a ConstructorInfo and the only Emit overload that takes a ConstructorInfo has no support for passing in additional parameter types.


Solution

  • Okay, After reading this post

    I discovered an incredibly easier method of doing this: You can get a token for the constructor with optional arguments from your method builder. Why this is so unbelievably undocumented is a mystery. A similar version of the program in my previous answer is below that does the same thing but with just using this get methodtoken api. This is much easier!

    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);