[1]System.Reflection.Emit

  在这个大数据/云计算/人工智能研发普及的时代,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的相关内容和应用,感谢阅读

原文地址:https://www.cnblogs.com/Leo_wl/p/12228474.html

时间: 2024-10-13 15:22:22

[1]System.Reflection.Emit的相关文章

EntityFramework动态创建模型:System.Reflection.Emit + Code First

动态创建Entity Framework模型并且创建数据库 使用System.Reflection.Emit+Code First model创建以下的一个实体类和DbContext并且创建数据库: 1 using System; 2 3 public class Blog 4 5 { 6 7 private int id; 8 9 private string name; 10 11 public int ID 12 13 { 14 15 get 16 17 { 18 19 return th

System.Reflection.Emit学习

C#反射发出System.Reflection.Emit学习 分享: 1 一.System.Reflection.Emit概述 Emit,可以称为发出或者产生.与Emit相关的类基本都存在于System.Reflection.Emit命名空间下.反射,我们可以取得形如程序集包含哪些类型,类型包含哪些方法等等大量的信息,而Emit则可以在运行时动态生成代码. 二.IL代码解析 以下代码为例: 1 static void Main(string[] args) 2 { 3 int i = 1; 4

参数计数不匹配,未处理System.Reflection.TargetParameterCountException

系统出现异常:参数计数不匹配,未处理System.Reflection.TargetParameterCountException, 系统会显示如下的异常信息,但异常信息往往与实际异常位置差十万八千量: 未处理 System.Reflection.TargetParameterCountException Message=参数计数不匹配. Source=mscorlib StackTrace: 在 System.Reflection.RuntimeMethodInfo.Invoke(Object

【C#基础】System.Reflection (反射)

在使用.NET创建的程序或组件时,元数据(metadata)和代码(code)都存储于"自成一体"的单元中,这个单元称为装配件.我们可以在程序运行期间访问这些信息.在System.Reflection中有这样一个class----Assembly,我们可以通过它来加载一个装配件.方法如下:Assembly assm=Assembly.LoadFrom(fileName);其中filename是要加载的装配件的文件名称(带路径).接下来,我们就可以通过使用System.Reflectio

C# System.Reflection (反射)

在使用.NET创建的程序或组件时,元数据(metadata)和代码(code)都存储于"自成一体"的单元中,这个单元称为装配件.我们可以在程序运行期间访问这些信息. 在System.Reflection中有这样一个class----Assembly,我们可以通过它来加载一个装配件.方法如下: Assembly assm=Assembly.LoadFrom(fileName); 其中filename是要加载的装配件的文件名称(带路径). 接下来,我们就可以通过使用System.Refle

c#中system.reflection反射的使用

簡單理解為:取得已經封裝的好的dll格式程式集里的所有方法和屬性. 步驟如下: 1.Assembly ass = Assembly.LoadFrom("路徑+程式名稱"); //如 C:\\NWEEPI.DLL ; 加载程序集,返回类型是一个Assembly 2.Type t = ass.GetType(dtDLL.Rows[0]["namespace"].ToString() + "." + dtDLL.Rows[0]["classn

异常:“System.Reflection.Metadata”已拥有为“System.Collections.Immutable”定义的依赖项

参考动态执行T4模板:https://msdn.microsoft.com/zh-cn/library/bb126579.aspx 我项目是.NET Framework 4.5控制台应用程序写的. 执行用例提示报错:Microsoft.CodeAnalysis未引用,如图截图 项目引用的Microsoft.VisualStudio.TextTemplating.14.0 版本是14.3.25407 在Nuget官网上只找到 Microsoft.CodeAnalysis版本为1.3.0-beta1

Error CS0579 Duplicate 'System.Reflection.AssemblyTitleAttribute' attribute

今天在引入ClassLibraryQikuo的时候突然报错 Error CS0579 Duplicate 'System.Reflection.AssemblyTitleAttribute' attribute 然后用text文本打开.csproj文件后添加代码成 <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netstandard2.0</TargetFr

System.Reflection.Binder.cs

ylbtech-System.Reflection.Binder.cs 1.返回顶部 1. #region 程序集 mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 // C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\mscorlib.dll #endregion usin