从 IL 构造方法引用集合
Reference a collection from IL constructed method
我正在使用反射构建动态方法。大多数教程和文档(例如 How to: Define and Execute Dynamic Methods or Creating method dynamically, and executing it)显示了一个非常简单的示例。
我试图找到一种从动态程序集中引用另一个程序集的方法。
例如,我希望能够使用 Reflection.Emit
.
构造以下函数
public static void f(int n)
{
int[] arr = new arr[n];
return arr.Max();
}
执行此操作的常用方法是什么?
获得有效 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
我正在使用反射构建动态方法。大多数教程和文档(例如 How to: Define and Execute Dynamic Methods or Creating method dynamically, and executing it)显示了一个非常简单的示例。
我试图找到一种从动态程序集中引用另一个程序集的方法。
例如,我希望能够使用 Reflection.Emit
.
public static void f(int n)
{
int[] arr = new arr[n];
return arr.Max();
}
执行此操作的常用方法是什么?
获得有效 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