.NET高级特性-Emit(2.2)属性
关于Emit的博客已经进入第四篇,在读本篇博文之前,我希望读者能先仔细回顾博主之前所编写的关于Emit的博文,从该篇博文开始,我们就可以真正的使用Emit,并把知识转化为实战,我也会把之前的博文链接放在下方,以方便读者阅读,大家也可以将自己的疑问或者指正写在评论当中,博主会积极进行回复。
ok,今天我们继续来探索C#-Emit中关于类的知识和应用,今天我们要来探索和挖掘关于C#属性的二三事,并且我们要开始使用Emit中关于类、字段和属性开启我们的第一个应用-动态创建匿名类
一、什么是属性?
属性-C#中让人既爱又恨的东西,爱的是C#当中因为有了属性,.NET开发者只需要一句话就可以完成对类的封装,根本不需要像其它语言写这么多东西,我们可以用java来比较一下
在C#当中我们定义一个实体属性
public string Title { get; set; }
在Java当中我们就需要这样定义
private String title; public String getTitle()
{
return title;
} public void setTitle(String value)
{
title = value;
}
在C#当中简简单单的一句话在Java当中就需要写一个字段将两个方法,当然我不是在贬低Java,只是表明Java没有在语法上为开发者提供便利,当然这些年Java的语法也在逐渐完善,从Java8开始逐渐加入了推断类型var/匿名委托等等优秀的语法。
扯的有点远了,当然C#中使用属性也有它的问题,首先是许多入门级的程序员把属性当成字段进行泛滥的使用,造成了C#类失去了封装性,没有了封装,有可能就会造成致命的漏洞,所以请刚入门的程序员请慎重使用属性,属性虽然好但是不要滥用,在你对属性不熟悉的时候,尤其要处理好它的set访问器,或者抛弃属性使用以下最原始的方法进行编写。
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#如何将最终的字段省略的全过程。
二、IL中的属性
简单讲完了属性是什么以及属性的本质,我们就要来简要说说IL中的属性,因为Emit当中最终编写的还是IL代码。在IL当中,属性或者自动属性它的原本面貌就会被还原,下面的样例看的就清清楚楚。
首先,我们先定义一个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_Title方法
- set_Title方法
- Title属性
我们双击查看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高级特性系列,感谢阅读!!!
.NET高级特性-Emit(2.2)属性的更多相关文章
- .NET高级特性-Emit(2)类的定义
在上一篇博文发了一天左右的时间,就收到了博客园许多读者的评论和推荐,非常感谢,我也会及时回复读者的评论.之后我也将继续撰写博文,梳理相关.NET的知识,希望.NET的圈子能越来越大,开发者能了解/深入 ...
- .NET高级特性-Emit(2.1)字段
在上篇blog写完的几天后,有读者反映写的过于复杂,导致无法有效的进行实践:博主在考虑到园子里程序员水平高低不一致的情况,所以打算放慢脚步,对类的一些内容进行详细的讲解,顺带的会写一些笔者所遇到过的E ...
- .NET高级特性-Emit(1)
在这个大数据/云计算/人工智能研发普及的时代,Python的崛起以及Javascript的前后端的侵略,程序员与企业似乎越来越青睐动态语言所带来的便捷性与高效性,即使静态语言在性能,错误检查等方面的优 ...
- 你应该知道的Vue高级特性
本文使用的Vue版本:2.6.10 Vue为我们提供了很多高级特性,学习和掌握它们有助于提高你的代码水平. 一.watch进阶 从我们刚开始学习Vue的时候,对于侦听属性,都是简单地如下面一般使用: ...
- 区分元素特性attribute和对象属性property
× 目录 [1]定义 [2]共有 [3]例外[4]特殊[5]自定义[6]混淆[7]总结 前面的话 其实attribute和property两个单词,翻译出来都是属性,但是<javascript高 ...
- javascript高级特性
01_javascript相关内容02_函数_Arguments对象03_函数_变量的作用域04_函数_特殊函数05_闭包_作用域链&闭包06_闭包_循环中的闭包07_对象_定义普通对象08_ ...
- Android TextView高级特性使用
TextView一般都是用来显示一段文本,这里说的高级特性主要是一些我们平常不太常用的属性.包括文字阴影.自定义字体.html嵌入多格式.字体加粗.插入图片.这些特性平时开发APP的时候,可能一般使用 ...
- Python的高级特性8:你真的了解类,对象,实例,方法吗
Python的高级特性1-7系列是本人从Python2过渡3时写下的一些个人见解(不敢说一定对),接下来的系列主要会以类级为主. 类,对象,实例,方法是几个面向对象的几个基本概念,其实我觉得很多人并不 ...
- Spring框架学习[IoC容器高级特性]
1.通过前面4篇文章对Spring IoC容器的源码分析,我们已经基本上了解了Spring IoC容器对Bean定义资源的定位.读入和解析过程,同时也清楚了当用户通过getBean方法向IoC容器获取 ...
随机推荐
- SpringCloud之Zuul配置问题
当通过网关去调用服务的时候,尤其是服务里面配置了熔断,会发现拿不到熔断返回的信息 hystrix: command: default: execution: isolation: thread: ti ...
- mysql数据迁徙详解
数据迁徙是每个后端都会遇到的工作之一,本文介绍了一些常见的数据迁徙方法与工具 mysqldump:数据结构不变的数据迁徙 导出数据 mysqldump -u root -p DATABASE_NAME ...
- ES6对象简洁语法
对象(object)是 JavaScript 最重要的数据结构.ES6 对它进行了重大升级,本章介绍数据结构本身的改变及语法应用细节. 1.属性的简洁表示法 ◆ ES6 允许直接写入变量和函数,作为对 ...
- Android应用程序权限说明
1.声明运行该应用本身所需要的权限 <!-- 声明该应用本身需要打电话的权限 --> <users-permission androd:name="android:perm ...
- net core WebApi——公用库April.Util公开及发布
前言 在之前鼓捣过一次基础工程April.WebApi后,就考虑把常用的类库打包做成一个公共类库,这样既方便维护也方便后续做快速开发使用,仓库地址:April.Util_github,April.Ut ...
- CVE-2019-13272Linuxkernel权限许可和访问控制问题漏洞
漏洞简介: Linuxkernel是美国Linux基金会发布的开源操作系统Linux所使用的内核. Linuxkernel5.1.17之前版本中存在安全漏洞,该漏洞源于kernel/ptrace.c文 ...
- python items和setdefault函数
items() dict = {'runoob': '菜鸟教程', 'google': 'Google 搜索'} print("Value : %s" % dict.setdefa ...
- Linux的中断可以嵌套吗?
本文系转载,著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 来源: 微信公众号linux阅码场(id: linuxdev) 问答 问:Linux的中断可以嵌套吗? 答:以前是可以 ...
- git上传项目到github远程库
最近在学习使用 git 上传管理项目,依照教程,建好了一个远程库,也实现了本地库与远程库的项目同步上传,但是在试着将本地库里的项目上传到另一个新建远程库时遇到了问题,一直上传不成功,经过一番查找摸索终 ...
- haproxy+keepalived练习
小的网站结构 说明:如果部署在云上,比如阿里云上,不需要自己部署keepalived,直接买阿里云的slb即可,slb然后分发流量到两台haproxy机器 一.先部署两个web服务器 编译安装ngin ...