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. 【记录】垃圾清理软件 便携版CleanMyPC破解版

    摘要 使用CleanMyPC保持您的PC清洁并像新的一样运行.它扫描整个计算机以清理垃圾文件,加速您的PC并提高其性能.CleanMyPC不仅仅是一台PC清洁工 - 它是关注计算机的必备工具.[有能力 ...

  2. 测试面试话题8:测试人员如何让开发少写bug?

    在测试过程中和不同开发合作,往往会发现一些bug都是大多数开发人员常出现的错误,为了帮助开发人员,也减少测试的重复工作量,非常有必要将以往出现的bug做整理,分析原因,让开发知道这些bug, 避免再次 ...

  3. Linux新手随手笔记1.3

    shell脚本的编写(划重点) #!/bin/bash                    脚本的声明信息 #sjsjdhsjdhh                  脚本的注释 ls -l    ...

  4. pip "Cannot uninstall 'six'. It is a distutils installed project..." 解决方法

    安装 mysql-connector-python 时,由于依赖包 six 之前已经安装过,但是不能自动更新到所需版本.有如下错误提示: pip "Cannot uninstall 'six ...

  5. JWT是什么鬼

    什么是JWT Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点 ...

  6. Core官方DI解析(5)-ServiceProviderEngine

    最后来看看前面一直说的Engine(工作引擎),工作引擎接口是IServiceProviderEngine在ServiceProvider的构造函数中看到了根据指定的Mode创建了不同的实现类,下面先 ...

  7. (light oj 1306) Solutions to an Equation 扩展欧几里得算法

    题目链接:http://lightoj.com/volume_showproblem.php?problem=1306 You have to find the number of solutions ...

  8. 石家庄地铁系统开发(java web版)(一)

    今天所完成的任务: 在Mysql数据库中创建了sjzsubstop表和sjzsubway表 sjzsubstop表用于录入所有站点名称和与之对应的stopid(作为主码) sjzsubway表用于录入 ...

  9. (六)jdk8学习心得之Stream流

    六.Stream流 1. 什么是stream流 现阶段,可以把stream流看成一个高级版的Iterator.普通的Iterator只能实现遍历,遍历做什么,就需要具体些功能代码函数了.而这个stre ...

  10. VUE如何实现切换页面时的过渡动画?

    最近再写页面的时候,感觉页面之间的切换有点生硬,所以查了一下文档看见了transition这个组建,很实用,故此在这里跟大家分享一下 --------------------------------- ...