Reflection.Emit使用[Good]
2009-02-07 10:39 宝宝合凤凰 阅读(968) 评论(0) 编辑 收藏 举报由于Emit的中文资料好像不是很多.现在记录一些使用方法吧,算心得吧.:P
Reflection.Emit的作用是能够在程序运行时动态生成Class,以及Field和Method.
这样带来的一个好处就是,可以在程序运行时产生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函数有返回值,而InvokeShow函数没有返回值,所以要把返回值给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( object));
//给其赋值,把第一个参数的值传给这个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);
这就是一些基本的Emit的使用..:P
to be continued...
反射发出(Reflection Emit)的应用小结
1.什么是的反射发出(Reflection Emit)System..Reflection.Emit命名空间嵌套在System.Reflection的下面,它可动态的构建程序集和类型的所有框架类的根,在需要时动态的产生代码。
注意:反射发出(reflection emit)并不能产生源代码。换句话说,你在这里的努力并不能创建VB.Net或者C#代码。相反,反射发出(reflection emit)类会创建MSIL op代码。
一般的,使用反射发出(reflection emit)可能会是这样子的步骤:
1. 创建一个新的程序集(程序集是动态的存在于内存中或把它们保存到磁盘上)。
2. 在程序集内部,创建一个模块(module)。
3. 在模块内部,创建一个类型。
4. 给类型添加属性和方法。
5. 产生属性和方法内部的代码
确切得说,当你使用Reflection.Emit类产生代码时,以上描述的是你实际中要遵循的过程。
反射发出(Reflection Emit)的应用详细步骤如下:
步骤一:创建程序集
a) 创建一个AssemblyName(用于唯一标识和命名程序集)。
b) 获取当前应用程序域的一个引用(使用应用程序域提供的方法,返回AssemblyBuilder对象)。
c) 通过调用AppDomain.DefineDynamicAssembly产生一个AssemblyBuilder对象实例。
我们首先创建一个AssemblyName实例,用于标识我们的程序集name.Name = "MyFirstAssembly";
ad.DefineDynamicAssembly(name,AssemblyBuilderAccess.Run);
步骤二:定义一个模块(Module)
这里,我们需要使用ModuleBuilder类,在之前创建的程序集(abuilder)里创建一个动态的模块。
abuilder.DefineDynamicModule("MyFirstModule");
步骤三:创建一个类(Class)
现在,我们需要使用TypeBuilder类添加到这个程集中了。
mbuilder.DefineType("MyFirstClass",TypeAttributes.Public | TypeAttributes.Class);
步骤四:添加一个方法(Method)
在之前创建的类型对象上(theClass)调用DefineMethod获取一个MethodBuilder实例的引用。DefineMethod携带四个参数:方法的名称,方法可能的属性(如:public,private等等),方法的参数以及方法的返回值。在子程序里,参数和返回值可以是void值。
Type[] param = new Type[2];//定义方法的两个参数
param[0] = typeof(System.Int32);
param[1] = typeof(System.Int32);
//下面创建方法
MethodBuilder methodBuilder =
theClass.DefineMethod("ReturnSum",MethodAttributes.Public,ret,param);
步骤五:产生方法里面的代码
假如方法ReturnSum是如下这样的:
{
return val1 + val2;
}
{
// 代码大小 8 (0x8)
.maxstack 2
.locals init ([0] int32 CS$00000003$00000000)
IL_0000: ldarg.1
IL_0001: ldarg.2
IL_0002: add
IL_0003: stloc.0
IL_0004: br.s IL_0006
IL_0006: ldloc.0
IL_0007: ret
} // end of method Class1::ReturnSum
所以,我们使用MethodBuilder.GetILGenerator()方法获取对应方法上的ILGenerator类的实例。
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Ldarg_2);
gen.Emit(OpCodes.Add);
gen.Emit(OpCodes.Stloc_0);
gen.Emit(OpCodes.Br_S);
gen.Emit(OpCodes.Ldloc_0);
gen.Emit(OpCodes.Ret);
步骤六:产生类的引用
到此,我们已经创建了方法,类,模块和程序集。为了得到这个类的一个引用,需要调用CreateType
步骤七:在反射中应用反射发出产生的代码
object ReturnSumInst = Activator.CreateInstance(ReturnSumClass);//创建实例化
object o = ReturnSumClass.InvokeMember("ReturnSum",BindingFlags.InvokeMethod,null,ReturnSumInst,null);//调用方法
Console.WriteLine("Sum:{0}",o.ToString());//显示结果
附:下表列出了System.Reflection.Emit下常用的类的参考
Namespace.Class |
System.Reflection.Emit.AssemblyBuilder |
主要用途 |
定义动态的.NET程序集:一种自我描述的 .NET内建块.动态程序集是通过反射发出特意产生的. 该类继承于System.Reflection.Assembly. |
范例 |
Dim ab As AssemblyBuilder |
Namespace.Class |
System.Reflection.Emit.ConstructorBuilder |
主要用途 |
用于创建和声明一个动态类的构造器.它囊括了有关构造器的所有信息,包括:名称,方法签名和主体代码.仅仅在你需要创建一个带参数的构造器或者需要覆盖父类构造器的默认行为的时候. |
范例 |
Dim ourClass As TypeBuilder = [module].DefineType("ourClass", _TypeAttributes.Public) |
Namespace.Class |
System.Reflection.Emit.CustomAttributeBuilder |
主要用途 |
用于创建动态类的自定义特性. |
Namespace.Class |
System.Reflection.Emit.EnumBuilder |
主要用途 |
定义和声明枚举. |
Namespace.Class |
System.Reflection.Emit.EventBuilder |
主要用途 |
为动态类创建事件. |
Namespace.Class |
System.Reflection.Emit.FieldBuilder |
主要用途 |
为动态类创建字段. |
Namespace.Class |
System.Reflection.Emit.ILGenerator |
主要用途 |
用于产生MSIL代码. |
范例 |
Dim gen As ILGenerator = someMethod.GetILGenerator()gen.Emit(OpCodes.Ldarg_0)gen.Emit(OpCodes.Ret) |
Namespace.Class |
System.Reflection.Emit.LocalBuilder |
主要用途 |
创建方法或构造器的局部变量. |
Namespace.Class |
System.Reflection.Emit.MethodBuilder |
主要用途 |
用于创建和声明动态类的方法. |
Namespace.Class |
System.Reflection.Emit.MethodRental |
主要用途 |
一个很实用的类,用于从别的类中交换一个方法到动态创建的类中。当你需要快速重建一个已经在其它地方存在的方法时,就显得非常有用。 |
Namespace.Class |
System.Reflection.Emit.ParameterBuilder |
主要用途 |
为方法的签名创建参数. |
Namespace.Class |
System.Reflection.Emit.PropertyBuilder |
主要用途 |
为动态的类型创建属性. |