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.构建程序集
在创建程序集之前,我们先要为它取个名字。
AssemblyName位于System.Reflection命名空间下,它代表程序集的名称。
然后我们就可以用上面的名字来创建一个程序集了:
AssemblyBuilderAccess.ReflectionOnly:
DefineDynamicAssembly有很多重载,比如上面的例子可以添加第三个参数用于作为生成的程序集要存放到的目录。关于其他重载形式,大家可以查阅MSDN。这里重点说说AssemblyBuilderAccess这个枚举。
它有以下几个值:
AssemblyBuilderAccess.ReflectionOnly:表示动态程序集只能用于反射获取元素据用,不能执行。
AssemblyBuilderAccess.Run:表示动态程序集是用于执行的。 AssemblyBuilderAccess.Save:表示动态程序集会被保存到磁盘上,不能立即执行。 AssemblyBuilderAccess.RunAndSave:表示动态程序集会被保存至磁盘并能立即执行。
2.创建模块
创建程序集后,就需要为程序集添加模块了,我们可以如下定义一个模块:
如果想把动态生成的程序集保存至磁盘(如本例),定义模块时模块所在文件的名称一定要和保存程序集(后面会提到)时提供的文件名称一样。
3.定义类
有了前面的准备工作,我们开始定义我们的类型:
DefineType还可以设置要定义的类的基类,要实现的接口等等。
4.定义类成员(方法,属性等等)
既然有了类,下面我们就为它添加一个SayHello方法吧:
"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这样生成:
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来完成类型的创建。
这样一个完整的类就算完成了。但为了能用reflector查看我们创建的动态程序集,我们选择将这个程序集保存下来。
如前面定义模块时所说,这里文件名字必须和模块保存到的文件一致,否则我们前面定义的模块和这个模块的一切就无家可归了。接下来,(如果在定义模块时未指定动态创建的程序要保存到哪个目录)我们就可以到 Debug目录下看看生成的Main.dll了,用Reflector打开可以看到:
大功告成。
三、不包含main的控制台程序
一直以来,应用程序(控制台,winform)都是从Main函数启动的,如果没有Main还能启动吗?答案是可以,下面就用emit来做这样一个控制台程序,完整代码如下:
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概览的更多相关文章
- System.Reflection.Emit学习
C#反射发出System.Reflection.Emit学习 分享: 1 一.System.Reflection.Emit概述 Emit,可以称为发出或者产生.与Emit相关的类基本都存在于Syste ...
- Emit学习(3) - OpCodes - 动态添加属性、构造函数、方法
上一篇介绍了 IL 的部分, 基础的部分, 暂时就介绍到那里了, 接下来要进入代码编写阶段了. 今天的主题是 在代码运行的过程中, 去动态的创建类, 属性, 方法. 来源:http://www.cnb ...
- Emit学习(2) - IL - 常用指令介绍
学习Emit必不可少的, 会使用到IL中间代码. 初见IL代码, 让我有一种汇编的感觉, 让我想起了, 大学时, 学习8051的汇编语言. 多的就不扯了, 直接进入正题, OpCodes指令集是不是有 ...
- Emit学习(1) - HelloWorld
之前看过Dapper(使用到了Emit), CYQ.Data(另一种思路,没有使用Emit)类的框架之后, 也想自己做一个小框架玩一下, 不过此时能力太过欠缺, 做不了Cyq.Data或者PDF.Ne ...
- Emit学习(4) - Dapper解析之数据对象映射(一)
感觉好久没有写博客了, 这几天有点小忙, 接下来会更忙, 索性就先写一篇吧. 后面估计会有更长的一段时间不会更新博客了. 废话不多说, 先上菜. 一.示例 1. 先建类, 类的名称与读取的表名并没有什 ...
- Emit学习(3) - OpCodes - 循环和异常
本来准备直接进入Dapper的, 但是昨天初步看了一下, 内容不少, Dapper不愧是一款成熟的框架, 里面对各种情况的考虑, 很实用, 不过这也使得我短时间内看不完, 所以得等几天了. 那就先来看 ...
- Emit学习(2) - IL - 对象的创建过程
上一篇的介绍中, 并没有介绍到对象的创建过程, 这一篇主要就介绍一下, 对象的创建过程. 其实熟悉了IL语法之后, 完全可以用Reflector反编译代码去查看. 而且正因为有这个工具, 可以对照着R ...
- Emit学习笔记
1,给字段设置值,并返回 static void Main(string[] args) { //给字段设置值,并返回 AssemblyName assemblyName = new Assembly ...
- C#反射发出System.Reflection.Emit学习
一.System.Reflection.Emit概述 Emit,可以称为发出或者产生.与Emit相关的类基本都存在于System.Reflection.Emit命名空间下.反射,我们可以取得形如程序集 ...
随机推荐
- html总结----------------------看这个就够了
HTML是我们学习Javaweb的第一步 很好地掌握门课是非常有必要的!下面就是我在听资深老师讲课的笔记!个人觉得非常不错!希望可以帮助到那些在学习javaweb路上的 朋友们!从今天 陆续的整理这门 ...
- Java语言使用HttpClient模拟浏览器登录
使用HttpClient来模拟浏览器登录网站,然后可以进行操作,比如发布信息等 第一步:获取实际的post网址,(不考虑复杂情况下) 1.需要使用到firefox的httpfox插件,httpfox中 ...
- 华为过滤字符串(java)
/*通过键盘输入一串小写字母(a~z)组成的字符串.请编写一个字符串过滤程序,若字符串中出现多个相同的字符,将非首次出现的字符过滤掉.比如字符串“abacacde”过滤结果为“abcde”.要求实现函 ...
- nyoj 括号匹配
这个方程有两种形式,本文采用 if(s[i]=s[j]) dp[i][j]=d[i-1][j-1] dp[i][j]=min(dp[i][k]+dp[k+1][j],dp[i][j]) (i=&l ...
- xp系统下网络打印机怎么设置
亲测,可行 打印机共享可以有效节约办公资源,提高办公效率.可是还有很多朋友不知道怎么设置,我们这里讲一下网络打印机的设置方法. 1.我们点开桌面左下角的开始菜单,选择“打印机和传真” 2.我们右击某个 ...
- Docker系列(一)安装
操作系统版本:Centos7 Docker版本:1.8 设置安装源 1 cat > /etc/yum.repos.d/docker.repo << -EOF 2 [dockerr ...
- POJ2192 - Zipper(区间DP)
题目大意 给定三个字符串s1,s2,s3,判断由s1和s2的字符能否组成字符串s3,并且要求组合后的字符串必须是s1,s2中原来的顺序. 题解 用dp[i][j]表示s1的前i个字符和s2的前j个字符 ...
- HDU5627--Clarke and MST (bfs+位运算)
http://www.cnblogs.com/wenruo/p/5188495.html Clarke and MST Time Limit: 2000/1000 MS (Java/Others) M ...
- MSSQL数字时间(timestamp)转换为DATETIME
很简单: , CAST('1970-01-01 00:00:00' AS datetime))
- Linux的五个查找命令:find,locate,whereis,which,type
使用电脑的时候,经常需要查找文件. 在Linux中,有很多方法可以做到这一点.国外网站LinuxHaxor总结了五条命令,你可以看看自己知道几条.大多数程序员,可能经常使用其中的2到3条,对这5条命令 ...