反射学习入门篇 (三)--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技术的使用:   

   

View Code
 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来实现,提高执行效率,这一点儿在下一篇文章中再继续探讨,坚持,每天进步一点点! 

posted @ 2013-04-05 15:29  Mervin  阅读(6092)  评论(2编辑  收藏