Reflection.Emit的作用是能够在程序运行的时候动态生成Class,Method和Field.
这样带来的好处是在程序运行的时候产生DynamicProxy,从而可以达到AOP拦截的作用(就是AOP.NET的实现原理).
由于Emit中提供了TypeBuilder(生成Class的类),MethodBuilder(生成Method的类)以及FieldBuilder(生成类的成员变量的类)等等.
由于MethodBuilder只能生成函数的申明,不能生成函数的执行代码,所以要想生成一个完整的函数,就不可避免的要用到ILGenerator类.
ILGenerator只能通过Emit(...)函数来增加直接的IL代码,所以使用Emit的时候也得明白一些关于IL的知识.
在编程中,对于函数的调用是经常的,像Win32汇编一样,调用一个函数前要将其参数压入到栈中...
IL中要求参数压入到栈中,要以从左到右的方式,也就是先把函数声明的最左边的参数压到栈中,再依次把其如的参数分别压到栈中,比如:
函数定义:
public void Show(int,string,object);
通过InvokeShow来调用这个函数,InvokeShow的定义如下:
public void InvokeShow(int,string,object)
ILGenerator methodIL=TypeBuilder.DefineMethod(...).GetGenerator();
除了静态函数外,其他所有的函数的实际的参数都会在最左边多一位,就是this.所以这里的参数应该是从1开始的.
//假设Show函数的MethodInfo是这个InvokeShow的类的一个成员变量,其FieldBuilder名称为show_MethodInfo;
//这里,我们先把对象压到栈中,由于是类的成员,所以前面要加this
methodIL.Emit(Opcodes.Ldarg_0);
methodIL.Emit(Opcodes.Ldfld,show_MethodInfo);
//参数入栈
methodIL.Emit(Opcodes.Ldarg_1);
methodIL.Emit(Opcodes.Ldarg_2);
methodIL.Emit(Opcodes.Ldarg_3);
//调用函数
methodIL.Emit(Opcodes.Callvirt,typeof(MethodInfo).GetMethod("Invoke"));
//由于MethodInfo.Invoke函数有返回值,而ncokeShow函数没有返回值,所以要把返回值给Pop出去,保持栈的平衡
methodIL.Emit(OpCodes.Pop);
methodIL.Emit(OpCodes.Ret);
上面就是一个简单的函数调用的Emit实现了.
上面的代码中使用到了FieldBuilder 的show_MethodInfo.现在我们看看如何给这个成员变量赋值.
函数定义如下:
public void Initial(MethodInfo showMethod);
IL代码:
methodIL.Emit(OpCodes.Ldarg_0);
methodIL.Emit(OpCodes.Ldarg_1);
methodIL.Emit(OpCodes.Stfld.show_MethodInfo);
methodIL.Emit(OpCodes.Ret);
这样,就可以给类的成员变量进行赋值了
现怎么生成函数中的临时变量了?
通过ILGenerator的DeclareLocal函数可以生成临时变量:
LocalBuilder local=methodIL.DeclareLocal(typeof(System.String));
//给其赋值,把第一个参数的值传给 这个local
methodIL.Emit(OpCodes.Ldarg_1);
methodIL.Emit(OpCodes.Stloc,local);
//使用这个变量
methodIL.Emit(OpCodes.Ldloc,local);
默认情况下,定义的LocalBuilder是从0开始按顺序来的,也就是说,如果local是定义的第一个LocalBuilder,上面的代码也就可以写成下面这样:
methodIL.Emit(OpCodes.Ldarg_1);
methodIL.Emit(OpCodes.Stloc,_0);
//使用这个变量
methodIL.Emit(OpCodes.Ldloc_0);