在这个大数据/云计算/人工智能研发普及的时代,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官方文档:https://docs.microsoft.com/zh-cn/dotnet

  .NET API浏览器:https://docs.microsoft.com/zh-cn/dotnet/api

  之后作者将继续讲解.NET Emit的相关内容和应用,感谢阅读

.NET高级特性-Emit(1)的更多相关文章

  1. .NET高级特性-Emit(2)类的定义

    在上一篇博文发了一天左右的时间,就收到了博客园许多读者的评论和推荐,非常感谢,我也会及时回复读者的评论.之后我也将继续撰写博文,梳理相关.NET的知识,希望.NET的圈子能越来越大,开发者能了解/深入 ...

  2. .NET高级特性-Emit(2.1)字段

    在上篇blog写完的几天后,有读者反映写的过于复杂,导致无法有效的进行实践:博主在考虑到园子里程序员水平高低不一致的情况,所以打算放慢脚步,对类的一些内容进行详细的讲解,顺带的会写一些笔者所遇到过的E ...

  3. .NET高级特性-Emit(2.2)属性

    关于Emit的博客已经进入第四篇,在读本篇博文之前,我希望读者能先仔细回顾博主之前所编写的关于Emit的博文,从该篇博文开始,我们就可以真正的使用Emit,并把知识转化为实战,我也会把之前的博文链接放 ...

  4. 你应该知道的Vue高级特性

    本文使用的Vue版本:2.6.10 Vue为我们提供了很多高级特性,学习和掌握它们有助于提高你的代码水平. 一.watch进阶 从我们刚开始学习Vue的时候,对于侦听属性,都是简单地如下面一般使用: ...

  5. ActiveMQ中的Destination高级特性(一)

    ---------------------------------------------------------------------------------------- Destination ...

  6. Python3学习(二)-递归函数、高级特性、切片

    ##import sys ##sys.setrecursionlimit(1000) ###关键字参数(**关键字参数名) ###与可变参数不同的是,关键字参数可以在调用函数时,传入带有参数名的参数, ...

  7. 云端卫士实战录 | Java高级特性之多线程

    <实战录>导语 一转眼作为一名Java开发者已经四年多时间了,说长不长说短不短,对于java的感情还是比较深的,主要嘛毕竟它给了我饭吃.哈哈,开个玩笑.今天我想借此机会来和大家聊聊Java ...

  8. javascript高级特性

    01_javascript相关内容02_函数_Arguments对象03_函数_变量的作用域04_函数_特殊函数05_闭包_作用域链&闭包06_闭包_循环中的闭包07_对象_定义普通对象08_ ...

  9. Visual Studio 2015 速递(4)——高级特性之移动开发

    系列文章 Visual Studio 2015速递(1)——C#6.0新特性怎么用 Visual Studio 2015速递(2)——提升效率和质量(VS2015核心竞争力) Visual Studi ...

随机推荐

  1. c语言1博客作业02

    c语言1博客作业02 这个作业属于哪个课程 C语言程序设计 这个作业的要求在哪 [作业要求](https://edu.cnblogs.com/campus/zswxy/SE2019-2/homewor ...

  2. HTTP Catcher

    HTTP Catcher HTTP Catcher 是一个 Web 调试工具.它可以拦截.查看.修改和重放来自 iOS 系统的 HTTP 请求. 你不需要连接电脑,HTTP Catcher 可以在后台 ...

  3. git jenkins 部署java项目

    1.Java项目部署基本概述:  1.什么是Java项目?  2.为什么Java项目需要使用Maven编译?  3.手动实现Java项目构建?  4.手动实现Java项目架构图? 源码包   jar包 ...

  4. 了解这一行的,腰包都鼓鼓的了,程序辅导,CS作业

    我们都知道计算机这类理科专业,可能你打开电脑聊几分钟微信,可能你就已经错过了成为程序员大佬的机会.就像数学专业的同学弯腰捡了一支笔,然后发现黑板被写满从此再也没有学懂数学.所以课后的作业自然也就没法顺 ...

  5. 闪讯 开启wifi教程

    这是我自己试了几次之后发现的,也不是什么技术活. 首先说下,我的比较是小米pro笔记本,一般笔记本都是自带wifi功能的.如果要开wifi的话,必须是用网线连接才可以,通过wifi连接网络就不能开移动 ...

  6. 面对对象高阶+反射+魔法方法+单例(day22)

    目录 昨日内容 组合 封装 property装饰器 多态 鸭子类型 今日内容 classmethod staticmethod 面对对象高级 isinstance issubclass 反射(重要) ...

  7. 设计模式(八)Abstract Factory模式

    抽象工厂的工作是将“抽象零件”组装为“抽象产品”.在抽象工厂模式中将会出现抽象工厂,它会将抽象零件组装为抽象产品.也就是说,我们并不关心零件的具体实现,而是只关心接口.我们仅适用该接口将零件组装起来成 ...

  8. (Git) 优秀Java,Vue项目推荐

    Java 1.spring-boot-pay 地址:小柒2012/spring-boot-pay 这是一个支付案例,提供了包括支付宝.微信.银联在内的详细支付代码案例,对于有支付需求的小伙伴来说,这个 ...

  9. C++智能指针类型转换

    #include <iostream> #include <memory> struct Base { int a; virtual void f() const { std: ...

  10. 文件I/O——文件打开函数(open/openat)

    一.open函数 1.函数原型:int open(const char *path,int oflag,.../* mode_t mode */); 2.头文件:#include <fcntl. ...