本文示例源代码或素材下载
之前的Hello World例子应该已经让我们对Emit有了一个模糊的了解,那么Emit到底是什么样一个东西,他又能实现些什么功能呢?昨天查了点资料,大致总结了下,由于才开始学习肯定有不完善的地方,希望大家能够批评指正。
1. 什么是反射发出(Reflection Emit)
Emit应该是属于反射中的一个比较高级的功能,说到反射大家应该都不陌生,反射是在运行时发现对象的相关信息,并且执行这些对象(创建对象实例,执行对象上的方法)。这个功能是由.NET的System.Reflection命名空间的类所提供的。简单的说,它们不仅允许你浏览一个程序集暴露的类、方法、属性和字段,而且还允许你创建一个类型的实例以及执行这些类型上的方法(调用成员)。这些特性对于在运行时对象发现,已经很了不起了,但.NET的反射机制并没有到此结束。反射还允许你在运行时构建一个程序集,并且可以创建全新的类型。这就是反射发出(reflection emit)。
使用Emit可以从零开始,动态的构造程序集和类型,在需要时动态的生成代码,提高程序的灵活性。有了这些功能,我们可以用其来实现一些典型的应用,如:
l 动态代理(AOP);
l 减少反射的性能损失(Dynamic Method等);
l ORM的实现;
l 工具及IDE插件的开发;
l 公共代码安全模块的开发。
2. 使用Emit的完整流程
使用Emit一般包括以下步骤:
1) 创建一个新的程序集(可以选择存在与内存中或者持久化到硬盘);
2) 在程序集内创建一个模块;
3) 在模块内创建动态类;
4) 给动态类添加动态方法、属性、事件,等;
5) 生成相关的IL代码;
6) 返回创建出来的类型或持久化到硬盘中。
当然如果你只是想要创建一个Dynamic Method 那么可以直接使用之前HelloWorld例子中使用的DynamicMethod类来创建一个动态方法,并在构造函数时传入它所依附的类或者模块。看了这个流程,相信大家已经对用使用Emit来创建动态类型的过程有了一个直观的认识,下面我们就通过实现一个求斐波那契数列的类来加深对这一流程的了解。
在开始我们的例子之前,先给大家介绍一款反编译软件Reflector,使用这个软件可以给我们编写IL代码提供很大的帮助。
接下来我们按照上面所说的流程来创建我们的斐波那契类:
第一步:构建程序集
要构建一个动态的程序集,我们需要创建一个AssemblyBuilder对象,AssemblyBuilder类是整个反射发出工作的基础,它为我们提供了动态构造程序集的入口。要创建一个AssemblyBuilder对象,需要使用AppDomain的DefineDynamicAssembly方法,该方法包括两个最基本的参数:AssemblyName和AssemblyBuilderAccess前者用来唯一标识一个程序集,后者用来表示动态程序集的访问方式,有如下的成员:
成员名称 | 说明 |
Run | 表示可以执行但不能保存此动态程序集。 |
Save | 表示可以保存但不能执行此动态程序集。 |
RunAndSave | 表示可以执行并保存此动态程序集。 |
ReflectionOnly | 表示在只反射上下文中加载动态程序集,且不能执行此程序集。 |
在这里我们选择使用RunAndSave,完整的代码如下:
#region Step 1 构建程序集
//创建程序集名
AssemblyName asmName = new AssemblyName("EmitExamples.DynamicFibonacci");
//获取程序集所在的应用程序域
//你也可以选择用AppDomain.CreateDomain方法创建一个新的应用程序域
//这里选择当前的应用程序域
AppDomain domain = AppDomain.CurrentDomain;
//实例化一个AssemblyBuilder对象来实现动态程序集的构建
AssemblyBuilder assemblyBuilder = domain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave);
#endregion
第二步:定义模块(Module)
与第一步类似,要定一个动态模块,我们需要创建一个ModuleBuilder对象,通过AssemblyBuilder对象的DefineDynamicModule方法,;要传入模块的名字(如果要持久化到硬盘,那么还需要传入要保存的文件的名字,这里就是我们的程序集名),这里我们使用程序集名作为模块名字:
#region Step 2 定义模块
<