Spring 框架中的注入代码,以及自动生成对接口的实现,则根据il代码注入

Emit学习(1)-Emit概览

一、Emit概述

  Emit,可以称为发出或者产生。在Framework中,与Emit相关的类基本都存在于System.Reflection.Emit命名空间下。可见Emit是作为反射的一个元素存在的。说道反射,大家应该都不陌生,它允许我们查看程序集的元素据,从而取得形如程序集包含哪些类型,类型包含哪些方法等等大量的信息。但是反射也仅能够‘看’,而Emit则可以在运行时动态生成代码。接下来就来看看如何用Emit生成代码。

二、动态生成代码

  首先需要明确的是这里的代码并不是我们时常提到的C#,VB等源代码,而是IL代码。既然是IL代码,那学习Emit是不是要先对IL很熟悉呢?诚然,熟悉IL代码对Emit学习会大有帮助,但是不懂也没关系,因为IL和高级语言一样,也是有一些相对固定的语法结构组成,不可能在一个IL程序里表述if是一个样子而到另一个程序却变成了另一个样子。所以只要多用,多记,很快就能掌握这些东西。

  其次如C#,VB等程序会包含程序集,模块,类,方法,属性等元素一样,Emit生成的代码也包括这些元素。以下介绍Emit生成代码的基本流程:

  1.构建程序集

  在创建程序集之前,我们先要为它取个名字。

var asmName = new AssemblyName("Test");

  AssemblyName位于System.Reflection命名空间下,它代表程序集的名称。

  然后我们就可以用上面的名字来创建一个程序集了:

var asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave);

AssemblyBuilderAccess.ReflectionOnly:  
DefineDynamicAssembly有很多重载,比如上面的例子可以添加第三个参数用于作为生成的程序集要存放到的目录。关于其他重载形式,大家可以查阅MSDN。这里重点说说AssemblyBuilderAccess这个枚举。  
它有以下几个值:  
AssemblyBuilderAccess.ReflectionOnly:表示动态程序集只能用于反射获取元素据用,不能执行。  
AssemblyBuilderAccess.Run:表示动态程序集是用于执行的。   AssemblyBuilderAccess.Save:表示动态程序集会被保存到磁盘上,不能立即执行。 AssemblyBuilderAccess.RunAndSave:表示动态程序集会被保存至磁盘并能立即执行。

  2.创建模块

  创建程序集后,就需要为程序集添加模块了,我们可以如下定义一个模块:

var mdlBldr = asmBuilder.DefineDynamicModule("Main", "Main.dll");

  如果想把动态生成的程序集保存至磁盘(如本例),定义模块时模块所在文件的名称一定要和保存程序集(后面会提到)时提供的文件名称一样。

  3.定义类

  有了前面的准备工作,我们开始定义我们的类型:

var typeBldr = mdlBldr.DefineType("Hello",TypeAttributes.Public);

  DefineType还可以设置要定义的类的基类,要实现的接口等等。

  4.定义类成员(方法,属性等等)

  既然有了类,下面我们就为它添加一个SayHello方法吧:

var methodBldr = typeBldr.DefineMethod( 
"SayHello", 
MethodAttributes.Public, 
null,//return type 
null//parameter type );

  该方法的原型为public void SayHell();

  方法签名已经生成好了,但方法还缺少实现。在生成方法的实现前,必须提及一个很重要的概念:evaluation stack。在.Net下基本所有的操作都是通过入栈出栈完成的。这个栈就是evaluation stack。比如要计算两个数(a,b)的和,首先要将a放入evaluation stack中,然后再将b也放入栈中,最后执行加法时将弹出栈顶的两个元素也就是a和b,相加再将结果推送至栈顶。

  Console.WriteLine("Hello,World")可以用Emit这样生成:

var il = methodBldr.GetILGenerator();//获取il生成器 
il.Emit(OpCodes.Ldstr,"Hello, World"); 
il.Emit(OpCodes.Call,typeof(Console).GetMethod("WriteLine",new Type[]{typeof(string)})); 
il.Emit(OpCodes.Ret);

  OpCodes枚举定义了所有可能的操作,这里用到了:

  ldStr:加载一个字符串到evaluation stack。

  Call:调用方法。

  Ret:返回,当evaluation stack有值时会返回栈顶值。

  完成上面的步骤,一个类型好像就已经完成了。事实上却还没有,最后我们还必须显示的调用CreateType来完成类型的创建。

typeBldr.CreateType();

  这样一个完整的类就算完成了。但为了能用reflector查看我们创建的动态程序集,我们选择将这个程序集保存下来。

asmBuilder.Save("Main.dll");

   如前面定义模块时所说,这里文件名字必须和模块保存到的文件一致,否则我们前面定义的模块和这个模块的一切就无家可归了。接下来,(如果在定义模块时未指定动态创建的程序要保存到哪个目录)我们就可以到 Debug目录下看看生成的Main.dll了,用Reflector打开可以看到:

大功告成。

  三、不包含main的控制台程序

  一直以来,应用程序(控制台,winform)都是从Main函数启动的,如果没有Main还能启动吗?答案是可以,下面就用emit来做这样一个控制台程序,完整代码如下:

var asmName = new AssemblyName("Test"); 
var asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly( 
asmName, 
AssemblyBuilderAccess.RunAndSave); 
var mdlBldr = asmBuilder.DefineDynamicModule("Main", "Main.exe"); 
var typeBldr = mdlBldr.DefineType("Hello", TypeAttributes.Public); 
var methodBldr = typeBldr.DefineMethod( 
"SayHello", 
MethodAttributes.Public | MethodAttributes.Static, 
null,//return type 
null//parameter type 
); 
var il = methodBldr.GetILGenerator();//获取il生成器 
il.Emit(OpCodes.Ldstr,"Hello, World"); 
il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[]{typeof(string)})); 
il.Emit(OpCodes.Call, typeof(Console).GetMethod("ReadLine")); 
il.Emit(OpCodes.Pop);//读入的值会被推送至evaluation stack,而本方法是没有返回值的,因此,需要将栈上的值抛弃 
il.Emit(OpCodes.Ret); 
var t = typeBldr.CreateType(); 
asmBuilder.SetEntryPoint(t.GetMethod("SayHello")); 
asmBuilder.Save("Main.exe");

运行生成的Main.exe效果如下:

Emit 自动生成IL代码,注入代码的更多相关文章

  1. EF自动生成的模型edmx代码分析

    edmx代码分析 本文分析Entity Framework从数据库自动生成的模型文件代码(扩展名为edmx). 1. 概述 本文使用的数据库结构尽量简单,只有2个表,一个用户表和一个分公司表(相当于部 ...

  2. 利用strut2标签自动生成form前端验证代码

    利用strut2标签自动生成form前端验证代码,使用到的技术有1.struts2标签,如<s:form> <s:textfieled>2.struts2读取*Validati ...

  3. 利用mybatis-generator自动生成数据持久化的代码

    MyBatis生成器简介 MyBatis Generator(MBG)是MyBatis MyBatis 和iBATIS的代码生成器.它将生成所有版本的MyBatis的代码,以及版本2.2.0之后的iB ...

  4. MyBatis Generator作为maven插件自动生成增删改查代码及配置文件例子

    什么是MyBatis Generator MyBatis Generator (MBG) 是一个Mybatis的代码生成器,可以自动生成一些简单的CRUD(插入,查询,更新,删除)操作代码,model ...

  5. Java自动生成asmx的webservice代码

    第一种方式:针对CXF自动生成的代码对响应类大小写区别问题,可以使用此方法. 工具为Eclipse. 新建Web Service Client. 输入地址点击finish即可自动生成. 调用方式: p ...

  6. Generator自动生成DAO和POJO代码

    一  添加相关插件 <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>myba ...

  7. 一键自动生成 java junit 测试代码神器 gen-test-plugin 入门介绍

    gen-test-plugin 我们日常编写代码的过程中,经常需要为代码编写测试案例. 随着对代码质量的要求越来越高,很多公司开始通过代码的测试覆盖率作为 QA 的一个评定指标. 本框架可以一键生成所 ...

  8. MyBatis Generator自动生成MyBatis的映射代码

    MyBatis Generator大大简化了MyBatis的数据库的代码编写,有了一个配置文件,就可以直接根据表映射成实体类.Dao类和xml映射.资源地址:MyBatis项目地址:http://my ...

  9. php自动生成mysql的触发代码。

    如果公司里有上百个表要做触发器,如果手动写代码的话.很累,所以今天写了一个小程序, <?php $dbname = 'test';//数据库 $tab1 = 'user'; //执行的表 $ta ...

随机推荐

  1. 解决FileZilla访问手机ftp服务只能删除浏览文件不能下载文件的问题

    用了Linux系统之后,很多资源都不方便获取, 因为很多资料都是放在百某某盘上面. 无意中看到我手机有FTP服务,想到我电脑装有FileZilla,可以访问手机了 然后就连接上了. 但是浏览的时候发现 ...

  2. Linux:Day20(上) openssh和CA

    ssh:secure shell protocol,22/tcp,安全的远程登陆 OpenSSH:ssh协议的开源实现: dripbear:另一个开源实现: SSH协议版本 v1:基于CRC-32做M ...

  3. mybatis insertUseGeneratedKeys 返回主键为null

    package tk.mybatis.mapper.common.special; import org.apache.ibatis.annotations.InsertProvider; impor ...

  4. soamanager发布的Webservice服务,调用时出现http500报错

    最近再给薪酬那边发布ws服务时出现了报错,调用方反馈了errorCode:BEA-380002.在使用XMLspy工具去调用这个WSDL时候,则反馈http500的错误消息.如下图: 遇到这种问题我通 ...

  5. OSI七层模型的每一层都有哪些协议

    TCP/IP: 数据链路层:ARP,RARP 网络层: IP,ICMP,IGMP 传输层:TCP ,UDP,UGP 应用层:Telnet,FTP,SMTP,SNMP. OSI: 物理层:EIA/TIA ...

  6. Java 常见数据交换格式——xml、json、yaml

    目录 数据交换格式介绍 XML 使用DOM方式解析 使用SAX方式解析 使用DOM4J方式解析 使用JDOM方式解析 JSON 使用JSONObject方式将数据转换为JSON格式 利用JSONObj ...

  7. pycharm .sqlite文件拖动到Database里面为空

    pycharm .sqlite文件拖动到Database里面为空 查资料得到解决方法:

  8. Acdream1201 SuSu's Power

    题目:SuSu's Power 链接:http://acdream.info/problem?pid=1201 题意:一个人站在x轴原点上,初始方向向x轴正方向,由一个字符串来控制其运动,字符串由A. ...

  9. 使用excel整理脚本

    的时候需要通过excel数据初始化脚本,当数据过多的时候,脚本也就很多.这里记录一个平时用excel初始化脚本的小技巧. excel中在空单元格中写如下值: ="INSERT INTO db ...

  10. ISOMAP

    转载 https://blog.csdn.net/dark_scope/article/details/53229427# 维度打击,机器学习中的降维算法:ISOMAP & MDS 降维是机器 ...