关于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)属性的更多相关文章

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

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

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

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

  3. .NET高级特性-Emit(1)

    在这个大数据/云计算/人工智能研发普及的时代,Python的崛起以及Javascript的前后端的侵略,程序员与企业似乎越来越青睐动态语言所带来的便捷性与高效性,即使静态语言在性能,错误检查等方面的优 ...

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

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

  5. 区分元素特性attribute和对象属性property

    × 目录 [1]定义 [2]共有 [3]例外[4]特殊[5]自定义[6]混淆[7]总结 前面的话 其实attribute和property两个单词,翻译出来都是属性,但是<javascript高 ...

  6. javascript高级特性

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

  7. Android TextView高级特性使用

    TextView一般都是用来显示一段文本,这里说的高级特性主要是一些我们平常不太常用的属性.包括文字阴影.自定义字体.html嵌入多格式.字体加粗.插入图片.这些特性平时开发APP的时候,可能一般使用 ...

  8. Python的高级特性8:你真的了解类,对象,实例,方法吗

    Python的高级特性1-7系列是本人从Python2过渡3时写下的一些个人见解(不敢说一定对),接下来的系列主要会以类级为主. 类,对象,实例,方法是几个面向对象的几个基本概念,其实我觉得很多人并不 ...

  9. Spring框架学习[IoC容器高级特性]

    1.通过前面4篇文章对Spring IoC容器的源码分析,我们已经基本上了解了Spring IoC容器对Bean定义资源的定位.读入和解析过程,同时也清楚了当用户通过getBean方法向IoC容器获取 ...

随机推荐

  1. Django中ORM的优化

    1. exists( ) 什么时候用 count( ) 和 len( )什么时候合适, 用values( )或values_list( )只取需要的列的数据 iterator()对数据对象生成迭代器, ...

  2. Java多线程编程(五)定时器Timer

    一.定时器Timer的使用 在JDK库中Timer类主要负责计划任务的功能,也就是在指定的时间开始执行某一个任务.Timer类的主要作用就是设置计划任务,但封装任务的类确实TimerTask类,执行计 ...

  3. VS Code断点调试PHP超详细萌新教程

    AppServ安装 1. 下载 2. 安装,一路默认设置顺便设置sql密码即可.这里建议不要修改端口,后续教程默认80端口. 3.点我测试,有下图则恭喜你AppServ安装完成. Xdebug配置 1 ...

  4. CVE-2019-16097:Harbor任意管理员注册漏洞复现

    0x00 Harbor简介 Harbor是一个用于存储和分发Docker镜像的企业级Registry服务器,通过添加一些企业必需的功能特性,例如安全.标识和管理等,扩展了开源Docker Distri ...

  5. Jenkins 结合 ANT 发送测试报告

    全局变量配置 点击 ManageJenkins进入Jenkins 管理 点击 Global Tool Configuration 进入全局变量配置 配置 Ant ,Name 自己定义一个比较好理解的名 ...

  6. 【翻译】Prometheus 2.12.0 新特性

    Prometheus 2.12.0 现在(2019.08.17)已经发布,在上个月的 2.11.0 之后又进行了一些修正和改进. 在当前的 6 周发布周期中,每一个 Prometheus 版本都有比较 ...

  7. Java 实现一个 能够 进行简单的增删改查的 超市管理系统

    1. 首先编写一个 Fruitltem 的商品类, 描述 商品的基本信息. 代码如下: 保证详细, 运行的起来, 有什么 问题也可以评论留言. /* * 自定义类, 描述商品信息 * * 商品的属性: ...

  8. (八)golang--复杂类型之指针

    首先我们要明确:(1)基本数据类型:变量存的就是值,也叫值类型: (2)获取变量的地址,用&,例如var num int,获取num的地址:&num: (3)指针类型:变量存的是一个地 ...

  9. 如何编译安装Linux内核

    操作系统环境 VMware workstation15 Pro ubuntu18.04 LTS 待编译内核5.3.10版本 内核下载地址 kernel.org 环境配置 在正式编译前需要安装部分软件. ...

  10. 第5天,python之路

    已经过了两三天了,东西一点没来写,今天慢慢的写吧. 还是会按Alex Li的流程来学习,编写     写出自己的东西 常用的数据类型 简单的有,str ,int , 常用的:  列表list[], 字 ...