1

I am building a dynamic method using reflection. Most tutorials and documentation (e.g. How to: Define and Execute Dynamic Methods or Creating method dynamically, and executing it) show a very simple example.

I trying to find a way to reference another assembly from the dynamic assembly.

For example, I would like to be able to construct the following function by using Reflection.Emit.

public static void f(int n)
{
    int[] arr = new arr[n];
    return arr.Max();
}

What would be the common way of doing this?

Community
  • 1
  • 1
Igor Ševo
  • 5,459
  • 3
  • 35
  • 80
  • 1
    You have to be the compiler, generating the code for the `Enumerable.Max()` extension method. Use a decompiler like ildasm.exe to get a head-start. – Hans Passant Jan 22 '16 at 16:21

1 Answers1

4

The most reliable way to get valid IL instructions is to create your code in a high level language, compile it and decompile it. Next you can recreate the instructions with Reflection.Emit.

I changed your example function otherwise its not good testable because the result would be always the same:

public static int f(int n)
{
    int[] arr = Enumerable.Range(0, n).ToArray();
    return arr.Max();
}

Build as debug, ILDasm gives us (Release would result in far fewer instructions but there is not too much to see then):

.method public hidebysig static int32  f(int32 n) cil managed
{
  // Code size       25 (0x19)
  .maxstack  2
  .locals init ([0] int32[] arr,
           [1] int32 V_1)
  IL_0000:  nop
  IL_0001:  ldc.i4.0
  IL_0002:  ldarg.0
  IL_0003:  call       class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> [System.Core]System.Linq.Enumerable::Range(int32,
                                                                                                                                  int32)
  IL_0008:  call       !!0[] [System.Core]System.Linq.Enumerable::ToArray<int32>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>)
  IL_000d:  stloc.0
  IL_000e:  ldloc.0
  IL_000f:  call       int32 [System.Core]System.Linq.Enumerable::Max(class [mscorlib]System.Collections.Generic.IEnumerable`1<int32>)
  IL_0014:  stloc.1
  IL_0015:  br.s       IL_0017
  IL_0017:  ldloc.1
  IL_0018:  ret
} // end of method Program::f

Now you can recreate every aspect of this method with the MethodBuilder/DynamicMethod and the ILGenerator.

public static int fNew(int n)
{
    var da = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("tempAsm"),
        AssemblyBuilderAccess.RunAndSave);
    var dm = da.DefineDynamicModule("tempAsm", "tempAsm.dll");
    var dt = dm.DefineType("dynType");

    var d = dt.DefineMethod("f", MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig,
        CallingConventions.Standard, typeof (int), new[] {typeof (int)});
    var il = d.GetILGenerator();
    il.DeclareLocal(typeof (int[]));
    il.DeclareLocal(typeof (int));

    var range = typeof (Enumerable).GetMethod(nameof(Enumerable.Range));
    var toArray = typeof (Enumerable).GetMethod(nameof(Enumerable.ToArray)).MakeGenericMethod(typeof (int));
    var max = typeof (Enumerable).GetMethod(nameof(Enumerable.Max),
        new[] {typeof (IEnumerable<>).MakeGenericType(typeof (int))});

    if (range == null || toArray == null || max == null) throw new Exception();

    var branchTarget = il.DefineLabel();

    il.Emit(OpCodes.Nop);
    il.Emit(OpCodes.Ldc_I4_0);
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Call, range);
    il.Emit(OpCodes.Call, toArray);
    il.Emit(OpCodes.Stloc_0);
    il.Emit(OpCodes.Ldloc_0);
    il.Emit(OpCodes.Call, max);
    il.Emit(OpCodes.Stloc_1);
    il.Emit(OpCodes.Br_S, branchTarget);
    il.MarkLabel(branchTarget);
    il.Emit(OpCodes.Ldloc_1);
    il.Emit(OpCodes.Ret);

    var bakedType = dt.CreateType();

    da.Save("tempAsm.dll");
    var x = bakedType.GetMethod("f");

    return (int) x.Invoke(null, new object[] {n});
}

Test it:

static void Main(string[] args)
{
    Console.WriteLine(f(10));
    Console.WriteLine(fNew(10));
}

and it should work:

9
9
Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
thehennyy
  • 4,020
  • 1
  • 22
  • 31