目录
10.1 无参属性
10.2 有参属性
10.3 调用属性访问器方法时的性能
10.4 属性访问器的可访问性
10.5 泛型属性访问器方法
10.1 无参属性
面向对象设计和编程的重要原则之一就是数据封装,意味着类型的字段不应该公开,否则很容易因为不恰单使用字段而破坏对象的状态。
可将属性想象成智能字段,即背后有额外逻辑的字段。CLR支持静态,实例,抽象和虚属性。属性可用任意“可访问性”修饰符来标记,而且可以在接口中定义。
经常利用属性的get和set方法操纵类型中定义的私有字段。私有字段通常称为支持字段。但get和set方法并非一定要访问支持字段。没有支持字段的另一种典型的属性是运行时计算的只读属性。
10.1.1 自动实现的属性
如果只是为了封装一个支持字段而 创建属性,就可以使用自动实现的属性(Automatically Implemented Property, AIP)。
AIP缺点
字段声明语法可能包含初始化部分,所以要在一行代码中声明并初始化字段。但没有简单的语法初始化AIP。所以,必须在每个构造器方法中显示初始化每个AIP。
运行时序列化引擎将字段名持久存储到序列化的流中。AIP的支持字段名称由编译器决定,每次重新编译代码都可能更改这个名称。因此,任何类型只要含有一个AIP,就没有办法对该类型的实例进行反序列化。在任何想要序列化或反序列化的类型中,都不要使用AIP功能。
调试时不能在AIP的get或set方法上添加断电,所以不好检测应用程序在什么时候获取或设置这个属性。手动实现的属性可设置断点,查错更方便。
10.1.2 合理定义属性
属性可以只读或只写,而字段访问总是可读和可写的(一个例外是readonly字段仅在构造器中可写)。如果定义属性,最好同时为它提供get和set访问器方法。
属性方法可能抛出异常:字段访问永远不会。
属性不能作为out或ref参数传给方法,而字段可以
属性方法可能花较长时间执行,字段访问则总是立即完成。许多人使用属性是为了线程同步,这就可能造成线程永远终止。所以要线程同步就不要使用属性,而要使用方法。
连续多次调用,属性方法每次都可能返回不同的值,字段则每次都返回相同的值。
属性方法可能造成明显的副作用,字段访问则永远不会。类型的使用者应该能按照他选择的任何顺序设置类型定义的各个属性,而不会造成类型中(因为设置顺序的不同)出现不同的行为。
属性方法可能需要额外的内存,或者返回的引用并非指向对象状态一部分,造成对返回对象的修改作用不到原始对象身上。而查询字段返回的引用总是指向原始对象状态的一部分。
10.1.3 对象和集合初始化器
对象初始化器语法真正的好处在于,它允许在表达式的上下文(相对于语句的上下文)中编码,允许组合对个函数,进而增强了代码的可读性。
10.1.4 匿名类型
利用C#的匿名类型功能,可以用很简洁的语法来自动声明不可变的元组类型。
匿名类型经常与LINQ配合使用。可用LINQ执行查询,从而生成由一组对象构成的集合,这些对象都是相同的匿名类型。
匿名类型的实例不能泄露到方法的外部。方法原型不能接受匿名类型的参数,因为无法指定匿名类型。类型地,方法也不能返回对匿名类型的引用。虽然可以将匿名类型的实例视为一个Object,但没办法将Object类型的变量转型回匿名类型,因为不知道在匿名类型在编译时的名称。
10.1.5 System.Tuple类型
和匿名类型相似,Tuple创建好之后就不可变了(所有属性都只读)。
10.2 有参属性
有参属性的get访问器方法接受一个或多个参数,set访问器方法接受两个或多个参数。C#称为索引器。
C#使用数组风格的语法来公开有参属性(索引器)。可将索引器看成是C#开发人员对[]操作符的重载。
查看文档时,留意类是否提供了名为Item的属性,从而判断该类型是否提供了索引器。
C#允许一个类型定义多个索引器,只要索引器的参数集不同。不允许定义多个相同签名的索引器,因为它的语法不是通过名称来引用索引器,编译器不知道你引用了的是哪个索引器。
10.3 调用属性访问器方法时的性能
对于简单的get和set访问器方法,JIT编译器会将代码内联(嵌入)。这样一来,使用属性就没有性能上的损失。内联是指将方法(访问器方法)的代码直接编译到调用它的方法中。这就避免了在运行时发出调用所产生的开销,代价是编译好的方法变得更大。由于属性访问器方法包含的代码一般很少,所以对内联会使生成的本机代码变得更小,而且执行得更快。
10.4 属性访问器的可访问性
定义属性时,如果两个访问器方法需要不同的可访问性,C#要求必须为属性本身指定限制最小的可访问性。然后,两个访问器只能选择一个来使用限制较大的。
10.5 泛型属性访问器方法
C#不允泛型属性访问器方法。主要原因是概念上说不通,属性本来应该表示可供查询或蛇者的某个对象特征。一旦引用泛型类型的参数,就意味着有可能改变查询/设置行为。但属性不应该和行为沾边。公开对象的行为--无论是不是泛型--都应该定义方法而非属性。