在上一篇博文发了一天左右的时间,就收到了博客园许多读者的评论和推荐,非常感谢,我也会及时回复读者的评论。之后我也将继续撰写博文,梳理相关.NET的知识,希望.NET的圈子能越来越大,开发者能了解/深入.NET的本质,将工作做的简单又高效,拒绝重复劳动,拒绝CRUD。

ok,咱们开始继续Emit的探索。在这之前,我先放一下我往期关于Emit的文章,方便读者阅读。

一、基础知识

既然C#作为一门面向对象的语言,所以首当其冲的我们需要让Emit为我们动态构建类。

废话不多说,首先,我们先来回顾一下C#类的内部由什么东西组成:

(1) 字段-C#类中保存数据的地方,由访问修饰符、类型和名称组成;

(2) 属性-C#类中特有的东西,由访问修饰符、类型、名称和get/set访问器组成,属性的是用来控制类中字段数据的访问,以实现类的封装性;在Java当中写作getXXX()和setXXX(val),C#当中将其变成了属性这种语法糖;

(3) 方法-C#类中对逻辑进行操作的基本单元,由访问修饰符、方法名、泛型参数、入参、出参构成;

(4) 构造器-C#类中一种特殊的方法,该方法是专门用来创建对象的方法,由访问修饰符、与类名相同的方法名、入参构成。

接着,我们再观察C#类本身又具备哪些东西:

(1) 访问修饰符-实现对C#类的访问控制

(2) 继承-C#类可以继承一个父类,并需要实现父类当中所有抽象的方法以及选择实现父类的虚方法,还有就是子类需要调用父类的构造器以实现对象的创建

(3) 实现-C#类可以实现多个接口,并实现接口中的所有方法

(4) 泛型-C#类可以包含泛型参数,此外,类还可以对泛型实现约束

以上就是C#类所具备的一些元素,以下为样例:

public abstract classBar

{public abstract voidPrintName();

}public interfaceIFoo

{public T Name { get; set; }

}//继承Bar基类,实现IFoo接口,泛型参数Tpublic classFoo : Bar, IFoo

//泛型约束

where T : struct

{//构造器

public Foo(Tname):base()

{

_name=name;

}//字段

private T_name;//属性

public T Name { get => _name; set => _name =value; }//方法

public override voidPrintName()

{

Console.WriteLine(_name.ToString());

}

}

在探索完了C#类及其定义后,我们要来了解C#的项目结构组成。我们知道C#的一个csproj项目最终会对应生成一个dll文件或者exe文件,这一个文件我们称之为程序集Assembly;而在一个程序集中,我们内部包含和定义了许多命名空间,这些命令空间在C#当中被称为模块Module,而模块正是由一个一个的C#类Type组成。

所以,当我们需要定义C#类时,就必须首先定义Assembly以及Module,如此才能进行下一步工作。

二、IL概览

由于Emit实质是通过IL来生成C#代码,故我们可以反向生成,先将写好的目标代码写成cs文件,通过编译器生成dll,再通过ildasm查看IL代码,即可依葫芦画瓢的编写出Emit代码。所以我们来查看以下上节Foo所生成的IL代码。

从上图我们可以很清晰的看到.NET的层级结构,位于树顶层浅蓝色圆点表示一个程序集Assembly,第二层蓝色表示模块Module,在模块下的均为我们所定义的类,类中包含类的泛型参数、继承类信息、实现接口信息,类的内部包含构造器、方法、字段、属性以及它的get/set方法,由此,我们可以开始编写Emit代码了

三、Emit编写

有了以上的对C#类的解读和IL的解读,我们知道了C#类本身所需要哪些元素,我们就开始根据这些元素来开始编写Emit代码了。这里的代码量会比较大,请读者慢慢阅读,也可以参照以上我写的类生成il代码进行比对。

在Emit当中所有创建类型的帮助类均以Builder结尾,从下表中我们可以看的非常清楚

元素中文元素名称对应Emit构建器名称

程序集

Assembly

AssemblyBuilder

模块

Module

ModuleBuilder

Type

TypeBuilder

构造器

Constructor

ConstructorBuilder

属性

Property

PropertyBuilder

字段

Field

FieldBuilder

方法

Method

MethodBuilder

由于创建类需要从Assembly开始创建,所以我们的入口是AssemblyBuilder

(1) 首先,我们先引入命名空间,我们以上节Foo类为样例进行编写

using System.Reflection.Emit;

(2) 获取基类和接口的类型

var barType = typeof(Bar);var interfaceType = typeof(IFoo<>);

(3) 定义Foo类型,我们可以看到在定义类之前我们需要创建Assembly和Module

//定义类

var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Edwin.Blog.Emit"), AssemblyBuilderAccess.Run);var moduleBuilder = assemblyBuilder.DefineDynamicModule("Edwin.Blog.Emit");var typeBuilder = moduleBuilder.DefineType("Foo", TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit);

(4) 定义泛型参数T,并添加约束

//定义泛型参数

var genericTypeBuilder = typeBuilder.DefineGenericParameters("T")[0];//设置泛型约束

genericTypeBuilder.SetGenericParameterAttributes(GenericParameterAttributes.NotNullableValueTypeConstraint);

(5) 继承和实现接口,注意当实现类的泛型参数需传递给接口时,需要将泛型接口添加泛型参数后再调用AddInterfaceImplementation方法

//继承基类

typeBuilder.SetParent(barType);//实现接口

typeBuilder.AddInterfaceImplementation(interfaceType.MakeGenericType(genericTypeBuilder));

(6) 定义字段,因为字段在构造器值需要使用,故先创建

//定义字段

var fieldBuilder = typeBuilder.DefineField("_name", genericTypeBuilder, FieldAttributes.Private);

(7) 定义构造器,并编写内部逻辑

//定义构造器

var ctorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard, newType[] { genericTypeBuilder });var ctorIL =ctorBuilder.GetILGenerator();//Ldarg_0在实例方法中表示this,在静态方法中表示第一个参数

ctorIL.Emit(OpCodes.Ldarg_0);

ctorIL.Emit(OpCodes.Ldarg_1);//为field赋值

ctorIL.Emit(OpCodes.Stfld, fieldBuilder);

ctorIL.Emit(OpCodes.Ret);

(8) 定义Name属性

//定义属性

var propertyBuilder = typeBuilder.DefineProperty("Name", PropertyAttributes.None, genericTypeBuilder, Type.EmptyTypes);

(9) 编写Name属性的get/set访问器

//定义get方法

var getMethodBuilder = typeBuilder.DefineMethod("get_Name", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.SpecialName |MethodAttributes.Virtual, CallingConventions.Standard, genericTypeBuilder, Type.EmptyTypes);var getIL =getMethodBuilder.GetILGenerator();

getIL.Emit(OpCodes.Ldarg_0);

getIL.Emit(OpCodes.Ldfld, fieldBuilder);

getIL.Emit(OpCodes.Ret);

typeBuilder.DefineMethodOverride(getMethodBuilder, interfaceType.GetProperty("Name").GetGetMethod()); //实现对接口方法的重载

propertyBuilder.SetGetMethod(getMethodBuilder); //设置为属性的get方法//定义set方法

var setMethodBuilder = typeBuilder.DefineMethod("set_Name", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.SpecialName | MethodAttributes.Virtual, CallingConventions.Standard, null, newType[] { genericTypeBuilder });var setIL =setMethodBuilder.GetILGenerator();

setIL.Emit(OpCodes.Ldarg_0);

setIL.Emit(OpCodes.Ldarg_1);

setIL.Emit(OpCodes.Stfld, fieldBuilder);

setIL.Emit(OpCodes.Ret);

typeBuilder.DefineMethodOverride(setMethodBuilder, interfaceType.GetProperty("Name").GetSetMethod());//实现对接口方法的重载

propertyBuilder.SetSetMethod(setMethodBuilder);//设置为属性的set方法

(10) 定义并实现PrintName方法

//定义方法

var printMethodBuilder = typeBuilder.DefineMethod("PrintName", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual, CallingConventions.Standard, null, Type.EmptyTypes);var printIL =printMethodBuilder.GetILGenerator();

printIL.Emit(OpCodes.Ldarg_0);

printIL.Emit(OpCodes.Ldflda, fieldBuilder);

printIL.Emit(OpCodes.Constrained, genericTypeBuilder);

printIL.Emit(OpCodes.Callvirt,typeof(object).GetMethod("ToString", Type.EmptyTypes));

printIL.Emit(OpCodes.Call,typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));

printIL.Emit(OpCodes.Ret);//实现对基类方法的重载

typeBuilder.DefineMethodOverride(printMethodBuilder, barType.GetMethod("PrintName", Type.EmptyTypes));

(11) 创建类

var type = typeBuilder.CreateType(); //netstandard中请使用CreateTypeInfo().AsType()

(12) 调用

var obj = Activator.CreateInstance(type.MakeGenericType(typeof(DateTime)), DateTime.Now);

(objasBar).PrintName();

Console.WriteLine((objas IFoo).Name);

四、应用

上面的样例仅供学习只用,无法运用在实际项目当中,那么,Emit构建类在实际项目中我们可以有什么应用,提高我们的编码效率

(1) 动态DTO-当我们需要将实体映射到某个DTO时,可以用动态DTO来代替你手写的DTO,选择你需要的字段回传给前端,或者前端把他想要的字段传给后端

(2) DynamicLinq-我的第一篇博文有个读者提到了表达式树,而linq使用的正是表达式树,当表达式树+Emit时,我们就可以用像SQL或者GraphQL那样的查询语句实现动态查询

(3) 对象合并-我们可以编写实现一个像js当中Object.assign()一样的方法,实现对两个实体的合并

(4) AOP动态代理-AOP的核心就是代理模式,但是与其对应的是需要手写代理类,而Emit就可以帮你动态创建代理类,实现切面编程

(5) …

五、小结

对于Emit,确实初学者会对其感到复杂和难以学习,但是只要搞懂其中的原理,其实最终就是C#和.NET语言的本质所在,在学习Emit的同时,也是在锻炼你的基本功是否扎实,你是否对这门语言精通,是否有各种简化代码的应用。

保持学习,勇于实践;Write Less,Do More;作者之后还会继续.NET高级特性系列,感谢阅读!

https://www.cnblogs.com/billming/p/emit-study-class.html

emit python 作用_.NET高级特性-Emit(2)类的定义相关推荐

  1. Python学习笔记:高级特性

    前言 最近在学习深度学习,已经跑出了几个模型,但Pyhton的基础不够扎实,因此,开始补习Python了,大家都推荐廖雪峰的课程,因此,开始了学习,但光学有没有用,还要和大家讨论一下,因此,写下这些帖 ...

  2. Python (五) 高级特性

    Python高级特性 一. 切片 python中提供了切片(Slice)操作符 , 可以方便的获取list或tuple中的某一段元素 . # -*- coding : utf-8 -*- #Pytho ...

  3. python切片迭代_Python高级特性 切片 迭代解析

    切片:方便截取list.tuple.字符串部分索引的内容 正序切片 语法:dlist = doList[0:3]表示,从索引0开始取,直到索引3为止,但不包括索引3.即索引0,1,2,正好是3个元素 ...

  4. 廖雪峰python学习笔记之高级特性

    写在前面 寒假时本科舍友的一句话点醒梦中人-你的python基础还没弄明白吧!猛地一想好像确实如此,还停留在随插随用的程度,并且对于迭代器,函数式编程等等,没有深刻理解,所以项目做起来也是空中楼阁,所 ...

  5. emit python 作用_PyQT5 emit 和 connect的用法详解

    对于PyQT4, PyQT5在一些使用上有着比较明显的变化有很大的变化,让人惊讶是在emit和connect上的一些变化比较有意思,相信也是QT为了更好的和Python相结合做的改进. 先上一张图: ...

  6. emit python 作用,PyQT5 emit 和 connect的用法详解

    对于PyQT4, PyQT5在一些使用上有着比较明显的变化有很大的变化,让人惊讶是在emit和connect上的一些变化比较有意思,相信也是QT为了更好的和Python相结合做的改进. 先上一张图: ...

  7. python generator长度_Python 高级特性之:生成器(generator)和迭代器(Iterator)

    前言: 之前学习Python自动化,接触了不少python的学习,对生成器印象尤其深,网上也看了很多介绍,下面主要是这些概念的个人学习整理(如侵删). 正文: 如要创建一个非常大的列表,受到内存限制, ...

  8. python generator输出_python 高级特性:Generator(生成器)

    格式对比: List Imprehensions 格式:[ 语句 for .. in ..] Generator 格式:(语句 for .. in ..) 区别:List存储的是具体内容,每个元素都占 ...

  9. Python学习札记(十七) 高级特性3 列表生成式

    参考:列表生成式 Note 1.List Comprehensions,即列表生成式,是Python中内置的非常强大的list生成式. eg.生成一个列表:[1*1, 2*2, ..., 10*10] ...

最新文章

  1. 从2D到3D的目标检测综述
  2. 解读刘强东关于人才的两个标准和5个层次
  3. 16.CSS选择器【下】
  4. C++ Map传递参数
  5. 【乱侃】How do they look them ?
  6. python12岁_12岁。Python操作Excel,12Python,excel
  7. python colorbar位置大小调整_python - matplotlib相邻子图:添加colorbar更改子图的大小 - 堆栈内存溢出...
  8. Python关键点常识
  9. java接口自动化框架_java接口自动化测试框架及断言详解
  10. micropython开发idethonny_esp32 micropython using uPycraft IDE
  11. 基于SSM的大学生兼职平台
  12. python视频网站项目_Python项目04 视频网站数据清洗整理和结论研究
  13. cad里面f命令用不了_南方CASS软件里CAD命令不能使用怎么办
  14. html画表盘 随时间转动,利用css3+js实现的可拖动简易表盘时钟
  15. cd40系列芯片_CD40,CD45系列芯片功能大全
  16. 第二人生的源码分析(3)程序入口点
  17. java基础总结(七十)--Java8中的parallelStream的坑
  18. xocde8打印出:Presenting view controllers on detached view controllers is discouraged SettingViewContro
  19. HDU 1546 Idiomatic Phrases Game 最短路
  20. echarts 立体柱状图(多个柱状图)

热门文章

  1. 成都昭觉寺客运站班期时刻表 班车信息
  2. postman脚本编写
  3. Machine Learning for hackers读书笔记(八)PCA:构建股票市场指数
  4. mono 直接运行exe linux,mono的安装(简单使用exe文件)
  5. VLFeat开源库初体验
  6. 手机里的计算机冷知识,手机上这些超实用的“冷知识”,你可能很多都不知道!...
  7. 分布式云不做流量明星,要做实力派
  8. 谷歌浏览器导出所有收藏书签
  9. 程序猿常用的查询网站或方式
  10. 如何清除服务器的缓存文件,服务器清理内存怎么清理缓存