10.2 有参属性
属性的get访问器不接受参数,因此称为无参属性。
C#还支持有参属性,他的get访问器方法接受一个或多个参数,set访问器方法接收两个或多个参数,C#称之为索引器。
C#使用数组风格的语法来公开有参属性,换句话说可以将索引器看成是C#开发者对[]操作符的重载。
CLR中的有参属性
索引器的set访问器同样包含了一个隐藏参数,在C#中称为value。
CLR本身不区分无参属性和有参属性。对CLR来说每个属性都只是类型定义中的定义的一对方法和一些元数据。
因为C#选择了this[]作为表达索引器的语法,所以C#只允许在对象的实例上定义索引器。C#不支持定义静态索引器属性,虽然CLR是支持静态有参属性的。
CLR以相同方式对待有参无参属性。编译器在索引器名称之前附加get_,set_前缀,从而自动生成这些访问器方法的名称。由于C#索引器不允许开发人员指定索引器名称,C#编译器不得不为访问器方法选择一个默认名称,get_Item、set_Item。
修改索引器的访问器方法的名字
如果为类型设计的索引器要由其他语言访问,可能需要更改get、set访问器方法所用的默认名称Item。
[IndexerName("Bit")] public Boolean this[Int32 bitPos]{...}
编译时,C#编译器会注意到IndexerName特性,会告诉编译器如何对方法和属性的元数据进行命名。
VB代码演示如何访问这个C#索引器
Dim ba as New BitArray(10) 'vb用()而不是[]指定数组元素 Console.WriteLine(ba(2)) 'vb还允许通过索引器名称来访问他 Console.WriteLine(ba.Bit(2))
定义多个索引的注意事项
C#允许一个类型定义多个索引器,只要索引器的参数集不同。C#不允许定义多个相同签名的索引器,因为C#编译器不知道你用的那个索引器,它的语法不是通过名称来引用索引器。
public Int32 this[Boolean b]{get{return 0;}} [IndexerName("Jeff")] public String this[Boolean b]{get{return null;}}
以上代码会报错,this具有相同参数类型的成员。
显然C#将索引器看成是对[]操作符的重载,而[]操作符不能用来消除具有不同方法名和相同参数集的有参属性的歧义。
BTW
String的索引器名称是Chars不是Item。这个只读属性允许从字符串中获得一个单独的字符。
10.3 调用属性访问器方法时的性能
对于简单的set、get方法,JIT编译器会将代码内联。
这样一来使用属性而不使用字段就没有性能上的损失。
内联是指将方法的代码,目前说的是访问器的方法的代码直接编译到调用他的方法中。
这就避免了在运行时发出调用所产生的开销,代价是编译好的方法变得更大。
由于属性访问器方法包含的代码一般很少,所以对内联会生成的本机diamagnetic变得更小执行更快。
10.4 属性访问器的可访问性
为get、set访问器方法可以指定一种可访问性。最常见的时提供公共get访问器和受保护set访问器。
public class SomeType { private String m_name; public String Name { get{return m_name;} protected set{m_name=value;} } }
定义属性时,如果两个访问器方法需要不同的可访问性,C#要求必须为属性本身指定限制最小的可访问性。然后两个访问器只能选择一个来使用限制较大的。
10.5 泛型属性访问器方法
属性本质是方法,C#和CLR允许泛型方法,但是C#不允许定义属性时引入他自己的泛型类型参数。
之所以不允许最主要的原因是概念上说不通。
属性本应表示可供查询或设置的某个对象特征。一旦引入泛型类型的参数就意味着有可能改变查询设置行为。
但属性不应该和行为沾边。公开对象的行为无论是不是泛型都应该定义访问而非属性。