The most reliable way to get valid IL instructions is to create your own high-level language code, compile it, and decompile it. You can then recreate the instructions using Reflection.Emit .
I changed your example function, otherwise it couldn’t be tested well, because the result would always be the same:
public static int f(int n) { int[] arr = Enumerable.Range(0, n).ToArray(); return arr.Max(); }
Build like a debug, ILDasm gives us (Release will result in far fewer instructions, but not so much to see):
.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 using the MethodBuilder / DynamicMethod and ILGenerator methods.
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}); }
Check this:
static void Main(string[] args) { Console.WriteLine(f(10)); Console.WriteLine(fNew(10)); }
and it should work:
9 9