关于Emit的博客已经进入第四篇,在读本篇博文以前,我但愿读者能先仔细回顾博主以前所编写的关于Emit的博文,从该篇博文开始,咱们就能够真正的使用Emit,并把知识转化为实战,我也会把以前的博文连接放在下方,以方便读者阅读,你们也能够将本身的疑问或者指正写在评论当中,博主会积极进行回复。html
ok,今天咱们继续来探索C#-Emit中关于类的知识和应用,今天咱们要来探索和挖掘关于C#属性的二三事,而且咱们要开始使用Emit中关于类、字段和属性开启咱们的第一个应用-动态建立匿名类 java
属性-C#中让人既爱又恨的东西,爱的是C#当中由于有了属性,.NET开发者只须要一句话就能够完成对类的封装,根本不须要像其它语言写这么多东西,咱们能够用java来比较一下git
在C#当中咱们定义一个实体属性程序员
public string Title { get; set; }
在Java当中咱们就须要这样定义github
private String title; public String getTitle() { return title; } public void setTitle(String value) { title = value; }
在C#当中简简单单的一句话在Java当中就须要写一个字段将两个方法,固然我不是在贬低Java,只是代表Java没有在语法上为开发者提供便利,固然这些年Java的语法也在逐渐完善,从Java8开始逐渐加入了推断类型var/匿名委托等等优秀的语法。app
扯的有点远了,固然C#中使用属性也有它的问题,首先是许多入门级的程序员把属性当成字段进行泛滥的使用,形成了C#类失去了封装性,没有了封装,有可能就会形成致命的漏洞,因此请刚入门的程序员请慎重使用属性,属性虽然好可是不要滥用,在你对属性不熟悉的时候,尤为要处理好它的set访问器,或者抛弃属性使用如下最原始的方法进行编写。ide
private string title; public string GetTitle() { return title; } public void SetTitle(string value) { title = value; }
ok,其实在上面与Java的比较当中咱们其实已经知道了属性是什么了,属性是对类中一类特殊方法的语法糖,这一类方法的功能是负责对字段的读取和设置,称之为get/set访问器,get方法用于获取字段的值,而set方法是对传入的值对字段进行赋值,固然,如何赋值和取值,就取决于你方法怎么写了。工具
那么有的读者就会有疑问,既然属性只是对于get/set访问器的语法糖,那么对应的字段跑哪里去了呢,其实这里面还运用了一种叫作自动属性的语法糖,这是在C#5.0以后增长的一种语法糖,对它详细的讲解能够查看个人博文《.NET高级特性-Emit(2.1)字段》,文章中详细说明了C#如何将最终的字段省略的全过程。ui
简单讲完了属性是什么以及属性的本质,咱们就要来简要说说IL中的属性,由于Emit当中最终编写的仍是IL代码。在IL当中,属性或者自动属性它的本来面貌就会被还原,下面的样例看的就清清楚楚。this
首先,咱们先定义一个Blog类,里面包含两个属性-Title和Content,表示标题和内容
public class Blog { public string Title { get; set; } public string Content { get; set; } }
接着,使用ildasm工具查看IL代码,ildasm工具博主有在《.NET高级特性-Emit(1)》中讲到如何使用,咱们能够看到仅仅一句话定义Title属性的话,C#为我生成四个东西,分别是
咱们双击查看Title属性,能够看到它的get和set直接连接向get_Title方法和set_Title方法
以后,咱们来观察下get_TItle方法和set_Title方法,结合上一章《.NET高级特性-Emit(2.1)字段》对字段操做,咱们很明显的看到,set_Title方法实现了对字段的赋值,而get_Title方法也正好对应了字段的取值
这就是在IL中呈现的属性的真正样貌,有了IL的理解,咱们就能开始咱们的Emit之旅了。
属性的定义其实很简单,属性真正的难点是在于如何编写get/set访问器,由于这才是属性的核心逻辑,并且对于自动属性来讲咱们须要定义字段/get访问器/set访问器和属性自己,因此博主打算用一个方法来实现自动属性的生成,已完成这一个过程的复用
首先,咱们来看一下方法定义,博主使用了扩展方法来为TypeBuilder扩展一个定义自动属性的方法,该方法只须要属性名称和类型,便可建立自动属性须要定义的字段/get访问器/set访问器和属性自己,工欲善其事必先利其器,有了这个方法咱们就能快速的建立自动属性
public static PropertyBuilder DefineAutomaticProperty(this TypeBuilder typeBuilder, string propertyName, Type propertyType) { //do something }
(1)而后,咱们定义属性的字段,因为是自动属性,因此字段的类型与属性类型相同,名称博主采用下划线+属性小写的方式定义
var fieldBuilder = typeBuilder.DefineField("_" + propertyName.ToLower(), propertyType, FieldAttributes.Private);
(2)以后,咱们定义属性,这个时候的属性是没有任何get/set访问器的
var propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.None, propertyType, Type.EmptyTypes);
(3)在定义完属性以后,咱们开始编写属性的get方法,get方法的内容是读取字段值并返回,如何编写能够参考个人文章《.NET高级特性-Emit(2.1)字段》中字段操做一节
//定义Get方法,返回属性类型,入参无 var getMethodBuilder = typeBuilder.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName, propertyType, Type.EmptyTypes); var getIL = getMethodBuilder.GetILGenerator(); getIL.Emit(OpCodes.Ldarg_0); //将字段放入栈顶 getIL.Emit(OpCodes.Ldfld, fieldBuilder); getIL.Emit(OpCodes.Ret);
(4)以后,咱们一样定义属性的set方法,内容为读取第一个参数并保存到字段,emit含义一样能够参考上一步get方法的文章
//定义Set方法,返回void,入参一个,类型为属性类型 var setMethodBuilder = typeBuilder.DefineMethod("set_" + propertyName, MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName, null, new Type[] { propertyType }); var setIL = setMethodBuilder.GetILGenerator(); setIL.Emit(OpCodes.Ldarg_0); //将第一个参数放入栈顶 setIL.Emit(OpCodes.Ldarg_1); //将栈顶元素弹出并保存到字段 setIL.Emit(OpCodes.Stfld, fieldBuilder); setIL.Emit(OpCodes.Ret);
(5)最后,咱们将get和set方法设置为属性的get和set
propertyBuilder.SetGetMethod(getMethodBuilder); propertyBuilder.SetSetMethod(setMethodBuilder);
(6)返回属性,咱们的定义自动属性方法就完成了,完整代码用户能够查看个人github:
return propertyBuilder;
在定义自动属性方法中,咱们定义了字段/属性/get访问器与set访问器,这样,咱们定义Blog类就很是的简单
首先,咱们只要先定义Blog类
var asmBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Edwin.Blog.Emit"), AssemblyBuilderAccess.Run); var moduleBuilder = asmBuilder.DefineDynamicModule("Edwin.Blog.Emit"); var typeBuilder = moduleBuilder.DefineType("Blog", TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.BeforeFieldInit);
而后直接使用上面咱们定义的扩展方法来定义自动属性
typeBuilder.DefineAutomaticProperty("Title", typeof(string)); typeBuilder.DefineAutomaticProperty("Content", typeof(string));
最后建立类型,就完成了咱们对Blog类的建立
typeBuilder.CreateTypeInfo().AsType();
最后建立并对属性赋值
dynamic user = Activator.CreateInstance(type); user.Title = "Emit高级特性-属性"; user.Content = "xxx";
便可在调试窗口看到以下结果
样例github地址:https://github.com/MJEdwin/edwin-blog-sample/blob/master/Edwin.Blog.Sample/Property/BlogEmit.cs
请读者思考,若是我定义一个方法,方法中传入类所须要的属性的名称和它对应的类型,咱们是否是就能够根据上述建立Blog的方式来建立一个只包含属性和字段的类,这样的类不就是咱们C#当中所说的匿名类了吗?
想一想,咱们日常开发当中何时使用匿名类居多?博主告诉你,没错就是Mapper,C#当中匿名类存在的意义就是能够实现实体对象到匿名对象的映射,使用最普遍的就是在Linq当中,那么若是咱们用Emit来建立匿名类,再在Linq中完成实体类到匿名类的映射,咱们我就能够动态DynamicLinq了吗?
因为篇幅缘由以及其中包含了表达式树的缘由,故博主就不将代码放在博文当中,有兴趣的读者能够查看个人github了解实现,博主将动态Select的流程图画在下方,知识丰富的小伙伴也能够自行实现。
本章讲解了属性是什么,Emit如何编写属性,以及属性最重要的一个应用-建立匿名类;不积跬步无以致千里,不积小流无以成江海,做为身处软件行业的咱们来讲,更须要这一份锲而不舍的积累,只有不断的积累-思考-积累-思考,才能从量变完成质变,写出更加优秀的代码和软件。
博主将继续更新.NET高级特性系列,感谢阅读!!!
原文出处:https://www.cnblogs.com/billming/p/emit-study-property.html