反射学习入门篇 (三)--Emit的使用
在之前的两篇文章中,了解了普通的反射技术使用方法及一些常用操作,结尾提到使用反射的性能问题,反射当中遇到的性能问题有很多种优化的解决方案,其中一种就是使用反射发出,即Emit技术的使用。
首先了解下Emit能做什么,MSDN上对Reflection.Emit是这样定义的:System.Reflection.Emit 命名空间包含允许编译器或工具发出元数据和Microsoft 中间语言 (MSIL)并可选择在磁盘上生成 PE 文件的类。直白的说就是通过Emit的使用,我们可以在程序中动态的去创建类型,方法,模块等到,在创建之后我们可以选择一次性使用,也可以把它保存下来,持久的使用。因此在我们需要动态创建类型,而又不希望永久驻留内存的话,我们就可以在创建类型后,动态编译一次,使用完毕之后就不用再保留。因此在性能上有很大的优势。了解了Emit能做什么,也就可以明白Emit的使用场景了,通常我们在下面几种情形时可以选择使用Emit来实现:
1. 运行中动态的创建类型、模块等,同时又需要提高效率(可以动态编译一次,然后就不用再处理了).
2 .延迟绑定对象的使用。
3 . 工具插件及IDE的开发等。
4. ORM的实现。
5. 减少反射的性能损失。
使用Emit常用的几个类如下:
类名 | 用途 |
AssemblyBuilder | 用来定义和创建动态的程序集 |
ConstructorBuilder | 用来创建动态类的构造函数 |
CustomAttributeBuilder | 用来创建用户自定义的特性 |
MethodBuilder | 用来创建动态类的方法,也可创建构造函数,因为构造函数本身也是一个特殊的方法 |
ModuleBuilder | 用来创建动态程序集中的模块 |
TypeBuilder | 用来定义和创建动态类的新实例 |
DynamicMethod | 用来创建可动态编译和执行的动态方法 |
ILGenerator | 用来生成中间语言,即MSIL指令 |
OpCodes | 提供 Microsoft 中间语言 (MSIL) 指令的字段表示形式 |
首先来看下使用Emit的一般步骤:
1. 创建一个程序集。
2. 在程序集内创建一个模块。
3. 在模块内创建动态类。
4. 为动态类添加动态方法,属性,事件,等等。
5. 生成相关的IL代码。
6. 返回创建的类型或是持久化保存到硬盘中。
接下来就通过一个实例来看下Emit技术的使用:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //得到当前的应用程序域 2 AppDomain appDm = AppDomain.CurrentDomain; 3 //初始化AssemblyName的一个实例 4 AssemblyName an = new AssemblyName(); 5 //设置程序集的名称 6 an.Name = "EmitLearn"; 7 //动态的在当前应用程序域创建一个应用程序集 8 AssemblyBuilder ab = appDm.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run); 9 //动态在程序集内创建一个模块 10 ModuleBuilder mb = ab.DefineDynamicModule("EmitLearn"); 11 //动态的在模块内创建一个类 12 TypeBuilder tb = mb.DefineType("HelloEmit", TypeAttributes.Public | TypeAttributes.Class); 13 //动态的为类里创建一个方法 14 MethodBuilder mdb = tb.DefineMethod("SayHelloEmit", MethodAttributes.Public, null, new Type[] { typeof(string) }); 15 16 //得到该方法的ILGenerator 17 ILGenerator ilG = mdb.GetILGenerator(); 18 //加载传入方法的参数到堆栈 19 ilG.Emit(OpCodes.Ldarg_1); 20 //调用Console.WriteLine方法,输出传入的字符 21 ilG.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) })); 22 ilG.Emit(OpCodes.Ret); 23 //创建类的Type对象 24 Type tp = tb.CreateType(); 25 //实例化一个类 26 object ob = Activator.CreateInstance(tp); 27 //得到类中的方法,通过Invoke来触发方法的调用.. 28 MethodInfo mdi = tp.GetMethod("SayHelloEmit"); 29 mdi.Invoke(ob, new object[] { "HelloEmit" });
上面的代码简单的展示了Emit的使用方法,然而在最后的方法调用上其实用了传统的反射Invoke方法,这一点在性能上还是存在损耗的,其实这里也可以通过Emit来实现,提高执行效率,这一点儿在下一篇文章中再继续探讨,坚持,每天进步一点点!