.NET高级特性-Emit(1)
在这个大数据/云计算/人工智能研发普及的时代,python的崛起以及javascript的前后端的侵略,程序员与企业似乎越来越青睐动态语言所带来的便捷性与高效性,即使静态语言在性能,错误检查等方面的优于静态语言。对于.neter来说,.net做为一门静态语言,我们不仅要打好.net的基本功,如基本类型/语法/底层原理/错误检查等知识,也要深入理解.net的一些高级特性,来为你的工作减轻负担和提高代码质量。
ok,咱们今天开始聊一聊.net中的emit。
一、什么是emit?
emit含义为发出、产生的含义,这是.net中的一组类库,命名空间为system.reflection.emit,几乎所有的.net版本(framework/mono/netcore)都支持emit,可以实现用c#代码生成代码的类库
二、emit的本质
我们知道.net可以由各种语言进行编写,比如vb,c++等,当然绝大部分程序员进行.net开发都是使用c#语言进行的,这些语言都会被各自的语言解释器解释为il语言并执行,而emit类库的作用就是用这些语言来编写生成il语言,并交给clr(公共语言运行时)进行执行。
我们先来看看il语言长什么样子:
(1) 首先我们创建一个hello,world程序
class program { static void main(string[] args) { console.writeline("hello world!"); } }
(2) 将程序编译成dll文件,我们可以看到在开发目录下生成了bin文件夹
(3) 向下寻找,我们可以看到dll文件已经生成,笔者使用netcore3进行开发,故路径为bin/debug/netcoreapp3.0
(4) 这时候,我们就要祭出我们的il查看神器了,ildasm工具
如何找到这个工具?打开开始菜单,找到visual studio文件夹,打开developer command prompt,在打开的命令行中键入ildasm回车即可,笔者使用vs2019进行演示,其它vs版本操作方法均一致
(5) 在dasm菜单栏选择文件->打开,选择刚刚生成的dll文件
(6) 即可查看生成il代码
有了ildasm的辅助,我们就能够更好的了解il语言以及如何编写il语言,此外,visual studio中还有许多插件支持查看il代码,比如jetbrains出品的resharper插件等,如果觉得笔者方式较为麻烦可以使用以上插件查看il代码
三、理解il代码
在上一章节中,我们理解了emit的本质其实就是用c#来编写il代码,既然要编写il代码,那么我们首先要理解il代码是如何进行工作的,il代码是如何完成c#当中的顺序/选择/循环结构的,是如何实现类的定义/字段的定义/属性的定义/方法的定义的。
il代码是一种近似于指令式的代码语言,与汇编语言比较相近,所以习惯于写高级语言的.neter来说比较难以理解
让我们来看看hello,world程序的il代码:
il_0000: nop il_0001: ldstr "hello world!" il_0006: call void [system.console]system.console::writeline(string) il_000b: nop il_000c: ret
我们可以把il代码看成栈的运行
第一条指令,nop表示不做任何事情,表示代码不做任何事情
第二条指令,ldstr表示将字符串放入栈中,字符串的值为“hello,world!”
第三条指令,call表示调用方法,参数为调用方法的方法信息,并把返回的结构压入栈中,使用的参数为之前已经入栈的“hello world!”,以此类推,如果方法有n个参数,那么他就会调取栈中n个数据,并返回一个结果放回栈中
第四条指令,nop表示不做任何事情
第五条指令,ret表示将栈中顶部的数据返回,如果方法定义为void,则无返回值
关于hello,world程序il的理解就说到这里,更多的指令含义读者可以参考微软官方文档,笔者之后也会继续对emit进行讲解和emit的应用
四、用emit类库编写il代码
既然il代码咱们理解的差不多了,咱们就开始尝试用c#来写il代码了,有了il代码的参考,咱们也可以依葫芦画瓢的把代码写出来了
(1) 引入emit命名空间
using system.reflection.emit;
(2) 首先我们定义一个main方法,入参无,返回类型void
//定义方法名,返回类型,输入类型 var method = new dynamicmethod("main", null, type.emptytypes);
(3) 生成il代码
//生成il代码 var ilgenerator = method.getilgenerator(); ilgenerator.emit(opcodes.nop); ilgenerator.emit(opcodes.ldstr,"hello world!"); ilgenerator.emit(opcodes.call, typeof(console).getmethod("writeline", new type[] { typeof(string) })); //寻找console的writeline方法 ilgenerator.emit(opcodes.nop); ilgenerator.emit(opcodes.ret);
(4) 创建委托并调用
//创建委托 var helloworldmethod = method.createdelegate(typeof(action)) as action; helloworldmethod.invoke();
(5)运行,即输出hello world!
五、小结
emit的本质是使用高级语言生成il代码,进而进行调用的的一组类库,依赖emit我们可以实现用代码生成代码的操作,即编程语言的自举,可以有效弥补静态语言的灵活性的缺失。
emit的性能非常好,除了第一次构建il代码所需要时间外,之后只要将操作缓存在计算机内存中,速度与手写代码相差无几
有许多著名.net类库均依赖于emit:
(.net json操作库)json.net/newtonsoft.json: github地址
(轻量orm)dapper:gituhb地址
(objecttoobjectmapper)emitmapper:github地址
(aop库)castle.dynamicproxy:github地址
学习emit:
.net官方文档:
.net api浏览器:
之后作者将继续讲解.net emit的相关内容和应用,感谢阅读
推荐阅读
-
java的高级特性——线程1
-
阿里Java学习路线:阶段 1:Java语言基础-Java语言高级特性:第22章:反射应用案例:课时101:反射实例化对象
-
阿里Java学习路线:阶段 1:Java语言基础-Java语言高级特性:第24章:反射与简单Java类:课时111:单级属性赋值
-
阿里Java学习路线:阶段 1:Java语言基础-Java语言高级特性:第18章: 输入与输出支持:课时84:打印流
-
阿里Java学习路线:阶段 1:Java语言基础-Java语言高级特性:第22章:反射应用案例:课时102:反射与工厂设计模式
-
阿里Java学习路线:阶段 1:Java语言基础-Java语言高级特性:第16章:字节流与字符流:课时75:Writer字符输出流
-
阿里Java学习路线:阶段 1:Java语言基础-Java语言高级特性:第17章: IO操作深入:课时83:RandomAccessFile
-
阿里Java学习路线:阶段 1:Java语言基础-Java语言高级特性:第26章:反射与代理设计模式:课时118:动态代理设计模式
-
阿里Java学习路线:阶段 1:Java语言基础-Java语言高级特性:第23章:反射与类操作:课时105:反射调用构造方法(含关系图-重要)
-
阿里Java学习路线:阶段 1:Java语言基础-Java语言高级特性:第17章: IO操作深入:课时82:管道流