一、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效果如下:

来源:http://www.csharpwin.com/csharpspace/10973r3457.shtml

Emit学习(1)-Emit概览的更多相关文章

  1. System.Reflection.Emit学习

    C#反射发出System.Reflection.Emit学习 分享: 1 一.System.Reflection.Emit概述 Emit,可以称为发出或者产生.与Emit相关的类基本都存在于Syste ...

  2. Emit学习(3) - OpCodes - 动态添加属性、构造函数、方法

    上一篇介绍了 IL 的部分, 基础的部分, 暂时就介绍到那里了, 接下来要进入代码编写阶段了. 今天的主题是 在代码运行的过程中, 去动态的创建类, 属性, 方法. 来源:http://www.cnb ...

  3. Emit学习(2) - IL - 常用指令介绍

    学习Emit必不可少的, 会使用到IL中间代码. 初见IL代码, 让我有一种汇编的感觉, 让我想起了, 大学时, 学习8051的汇编语言. 多的就不扯了, 直接进入正题, OpCodes指令集是不是有 ...

  4. Emit学习(1) - HelloWorld

    之前看过Dapper(使用到了Emit), CYQ.Data(另一种思路,没有使用Emit)类的框架之后, 也想自己做一个小框架玩一下, 不过此时能力太过欠缺, 做不了Cyq.Data或者PDF.Ne ...

  5. Emit学习(4) - Dapper解析之数据对象映射(一)

    感觉好久没有写博客了, 这几天有点小忙, 接下来会更忙, 索性就先写一篇吧. 后面估计会有更长的一段时间不会更新博客了. 废话不多说, 先上菜. 一.示例 1. 先建类, 类的名称与读取的表名并没有什 ...

  6. Emit学习(3) - OpCodes - 循环和异常

    本来准备直接进入Dapper的, 但是昨天初步看了一下, 内容不少, Dapper不愧是一款成熟的框架, 里面对各种情况的考虑, 很实用, 不过这也使得我短时间内看不完, 所以得等几天了. 那就先来看 ...

  7. Emit学习(2) - IL - 对象的创建过程

    上一篇的介绍中, 并没有介绍到对象的创建过程, 这一篇主要就介绍一下, 对象的创建过程. 其实熟悉了IL语法之后, 完全可以用Reflector反编译代码去查看. 而且正因为有这个工具, 可以对照着R ...

  8. Emit学习笔记

    1,给字段设置值,并返回 static void Main(string[] args) { //给字段设置值,并返回 AssemblyName assemblyName = new Assembly ...

  9. C#反射发出System.Reflection.Emit学习

    一.System.Reflection.Emit概述 Emit,可以称为发出或者产生.与Emit相关的类基本都存在于System.Reflection.Emit命名空间下.反射,我们可以取得形如程序集 ...

随机推荐

  1. QContester

    发个福利好了(求不吐槽名字 自己做的软件,(目前)效果大概如上图- 对于我这种比较喜欢参加一些奇怪比赛的人来说还是有用的. 原理-就是在后台开七个浏览器2333 按左边的按钮会开始抓取比赛,进度可以在 ...

  2. Spark On Yarn中spark.yarn.jar属性的使用

    今天在测试spark-sql运行在yarn上的过程中,无意间从日志中发现了一个问题: spark-sql --master yarn // :: INFO Client: Requesting a n ...

  3. 抓包工具Charles,anyproxy,mitmproxy等

    Charles:图形化界面,看着比较方便友好,也可以抓取https,不过电脑和手机都要下载证书,主要我的电脑上不能添加一添加就卡死 所以,抓取https的话,就用mitmproxy比较简单 1.安装C ...

  4. javascript --- 子对象访问父对象的方式

    在传统面向对象的编程语言里,都会提供一种子类访问父类的特殊语法,引文我们在实现子类方法往往需要父类方法的额外辅助.在这种情况下,子类通常会调用父类中的同名方法,最终以便完成工作. javascript ...

  5. js方法中的this

    比如有个function: function ServiceMy(services) { //存放this,用于调试用 var tmp_this = this; this.services = []; ...

  6. Analyzing .net core application with SonarQube Scanner for MSBuild

    SonarQube是管理代码质量一个开放平台,省略安装过程,下面介绍下如何使用sonarqube去扫描c# 代码. 前提:下载SonarQube Scanner for MSBuild.https:/ ...

  7. dotnet core命令

    dotnet run -----运行程序 dotnet publish -r centos-x64  -----发布程序 mkdri 文件名--->cd 文件名--->dotnet new ...

  8. Garbage Disposal(模拟垃圾装垃圾口袋)

    Garbage Disposal Description Enough is enough. Too many times it happened that Vasya forgot to dispo ...

  9. bzoj1067

    题意: 给你下雨量,让你判断每一句话是否正确 题解: 线段树 用来维护判断 代码: #include <cstdio> #include <cstring> #include ...

  10. Python全栈 项目(HTTPServer、PiP使用)

    pip是Python官方推荐的包管理工具   属于python的一部分            pip的使用    pip的安装             sudo apt-get install pyt ...