2016-01-22 45 views
1

我正在構建一個使用反射的動態方法。大多數教程和文檔(例如How to: Define and Execute Dynamic MethodsCreating method dynamically, and executing it)都顯示了一個非常簡單的示例。從IL中引用一個集合構造的方法

我試圖找到一種方法來引用動態程序集中的另一個程序集。

例如,我希望能夠通過使用Reflection.Emit構造以下功能。

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

這樣做的常見方法是什麼?

+1

你必須編譯,生成的代碼爲'可枚舉。最大()'擴展方法。使用像ildasm.exe這樣的反編譯器來獲得先機。 –

回答

4

獲得有效IL指令的最可靠的方法是以高級語言創建代碼,編譯並反編譯它。接下來,您可以使用Reflection.Emit重新創建說明。

我,否則改變你的榜樣功能一直不太好檢驗的,因爲其結果將是總是相同的:

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

建設成爲調試,ILDASM給我們(釋放將導致少得多的指令,但沒有太多那麼可以看到很多):

.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 

現在您可以使用MethodBuilder/DynamicMethod和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}); 
} 

測試:

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

,它應該工作:

9 
9 
相關問題