.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高级特性系列,感谢阅读!!!

原文地址:https://www.cnblogs.com/billming/p/emit-study-property.html

时间: 2024-10-07 02:28:43

.NET高级特性-Emit(2.2)属性的相关文章

Html5之高级-2 HTML5表单属性(属性介绍、属性详解)

一.属性介绍 属性介绍 - 有一些输入类型要求使用特定的属性才能显示效果,如前面提到过min,max,step. 其他输入类型需要使用一些属性来改进其他性能,或者决定验证过程的重要性.HTML5 标准中再原来的基础上增加了一些新的属性. - Placeholder 属性 - Nultiple 属性 - Autofocus 属性 - Form 属性 二.属性详解 Placeholder 属性 - Placeholder 属性通常用于search输入类型,也可以用在文本域.它表示一个简单提示.单词或

javascript高级特性

01_javascript相关内容02_函数_Arguments对象03_函数_变量的作用域04_函数_特殊函数05_闭包_作用域链&闭包06_闭包_循环中的闭包07_对象_定义普通对象08_对象_定义函数对象09_对象_内建对象10_原型_为函数对象增加属性或方法11_原型_利用函数对象本身重写原型12_继承_函数对象之间的继承13_继承_普通对象之间的继承 javascript高级特性(面向对象): * 面向对象:   * 面向对象和面向过程的区别:     * 面向对象:人就是对象,年龄\

JSP简明教程(五):高级特性

JSP过滤器 过滤器的作用是给web请求增加额外的逻辑,每个页面可以被多个过滤器进行处理.过滤器需要在web.xml文件中进行定义,语法如下.过滤器的执行顺序与filter-mapping的定义顺序相同. <filter> <filter-name>FilterName</filter-name> <filter-class>TestFilter</filter-name> <init-param> <param-name>

JavaScript【5】高级特性(作用域、闭包、对象)

笔记来自<Node.js开发指南>BYVoid编著 1.作用域 if (true) { var somevar = 'value'; } console.log(somevar); JavaScript的作用域完全是由函数决定的,if.for语句中的花括号不是独立的作用域. 1.1.函数作用域 作用域是通过函数来定义的,在一个函数中定义的变量只对这个函数内部可见,我们称为函数作用域.在函数中引用一个变量时,JavaScript会先搜索当前函数作用域,或者称为"局部作用域",

Python高级特性(3): Classes和Metaclasses(转)

原文:Python高级特性(3): Classes和Metaclasses 类和对象 类和函数一样都是Python中的对象.当一个类定义完成之后,Python将创建一个“类对象”并将其赋值给一个同名变量.类是type类型的对象(是不是有点拗口?). 类对象是可调用的(callable,实现了 __call__方法),并且调用它能够创建类的对象.你可以将类当做其他对象那么处理.例如,你能够给它们的属性赋值,你能够将它们赋值给一个变量,你 可以在任何可调用对象能够用的地方使用它们,比如在一个map中

Python高级特性(2):Closures、Decorators和functools(转)

原文:Python高级特性(2):Closures.Decorators和functools 装饰器(Decorators) 装饰器是这样一种设计模式:如果一个类希望添加其他类的一些功能,而不希望通过继承或是直接修改源代码实现,那么可以使用装饰器模式.简单来说 Python中的装饰器就是指某些函数或其他可调用对象,以函数或类作为可选输入参数,然后返回函数或类的形式.通过这个在Python2.6版本中被新 加入的特性可以用来实现装饰器设计模式. 顺便提一句,在继续阅读之前,如果你对Python中的

Python高级特性(1):Iterators、Generators和itertools(转)

译文:Python高级特性(1):Iterators.Generators和itertools [译注]:作为一门动态脚本语言,Python 对编程初学者而言很友好,丰富的第三方库能够给使用者带来很大的便利.而Python同时也能够提供一些高级的特性方便用户使用更为复杂的数据结构.本系 列文章共有三篇,本文是系列的第一篇,将会介绍迭代器.生成器以及itertools模块的相关用法.由于作者 Sahand Saba 列举的示例中有诸多专业的数学相关内容,因此翻译中有诸多不妥之处请大家指出,非常感谢

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

1.通过前面4篇文章对Spring IoC容器的源码分析,我们已经基本上了解了Spring IoC容器对Bean定义资源的定位.读入和解析过程,同时也清楚了当用户通过getBean方法向IoC容器获取被管理的Bean时,IoC容器对Bean进行的初始化和依赖注入过程,这些是Spring IoC容器的基本功能特性.Spring IoC容器还有一些高级特性,如使用lazy-init属性对Bean预初始化.FactoryBean产生或者修饰Bean对象的生成.IoC容器初始化Bean过程中使用Bean

JVM高级特性与实践(二):对象存活判定算法(引用) 与 回收

关于垃圾回收器GC(Garbage Collection),多数人意味它是Java语言的伴生产物.事实上,GC的历史远比Java悠远,于1960年诞生在MIT的Lisp是第一门真正使用内存动态分配和垃圾收集技术的语言.当Lisp尚在胚胎时期,开发人员就在思考GC需要完成的3件事情: 哪些内存需要回收? 什么时候回收? 如何回收? 目前GC早已解决了以上问题,内存的动态分配与内存回收机制已经相当成熟,一切似乎“自动化”起来.而开发人员仍旧需要了解GC和内存分配等底层知识,因为在排查各种内存溢出.内