您好,欢迎访问三七文档
第8章属性本章主要讨论联系紧密的两种特殊类型的类成员:属性(attribute)和索引器。他们通过增强类与C#类型的集成性以及提高类的灵活性来实现了扩展类功能的目的。属性为控制对类的实例数据的访问提供了简化的方式。索引器提供了一种机制,允许按照与索引数组相同的方式索引对象。这两种类成员紧密相关,原因在于它们都依赖于C#的另一个功能:访问器(accessor)。8.1属性初识对于属性,有人问这样的问题:属性是为了访问私有字段,为什么不直接把私有字段定义成公有的,而要设置公有属性?原因是公有属性是在任何外部类都可访问到的,任何人都可以随意修改public字段,很危险。如果设置成private,则只能通过属性修改。而在属性里是可以加代码来判断别人赋的值是否符合要求,不符合的可以拒绝赋值。这样就增加了安全性。下面我们就来学习一下有关属性的具体知识。8.1.1属性声明属性是另一种类型的类成员。它将字段和访问字段的方法组合在一起。它提供灵活的机制来读取、编写或计算私有字段的值。属性有自己的名称,并且包含get访问器和set访问器,它的声明格式如下图所示。属性修饰符类型属性名{get{//get访问器代码}set{//set访问器代码}}标识符,需符合标识符规范属性类型,任一类型皆可任一访问修饰符【示例8-1】以下代码演示的是声明属性的方法。首先定义私有字段pricePerSqYard,再声明属性PricePerSqYard。定义属性的目的在于便于一些私有字段的访问。因此建议大家在命名属性时采用与字段名相关联的属性名,如上所示字段名采用pricePerSqYard,而属性名采用PricePerSqYard。privatedoublepricePerSqYard;publicdoublePricePerSqYard{get{returnpricePerSqYard;}set{pricePerSqYard=value;}}8.1.2属性访问声明属性后,一旦出现属性名都将引发相应的访问器调用。【示例8-2】演示的是属性的访问方法。在上述程序中,TimePeriod类中定义了一个私有字段seconds,并且定义了管理对seconds的访问的属性Hours。通过实例化的对象调用了访问器,继而也就调用了属性。同时也可以发现,set访问器和get访问器的调用形式是相同的,均是t.Hours。但具体应什么时间调用哪个访问器,我们接下来就具体学习一下访问器的知识。8.1.3访问器属性都有访问器,它本质上是一种方法。这些访问器指定在它们的值被读取或写入时需执行的语句。1.访问器声明访问器的声明格式如下图所示。当属性声明包含extern修饰符时,该属性称为外部属性。因为外部属性声明不提供任何实际的实现,所以它的每个访问器声明都仅含有一个分号。abstract属性也是同样道理,非抽象和非外部属性则需要写明需执行的语句,如上图(右)所示。get/set{访问器体}必须包含在“{}”内一个分号一个块,指定调用访问器时需执行的语句abstract属性和extern属性非抽象和非外部属性访问器体2.get访问器get访问器本质上相当于必须返回属性类型的值的方法。执行get访问器相当于读取字段的值。【示例8-3】演示的是返回私有字段salary的值的get访问器。除了作为赋值的目标,当在表达式中引用属性时,将调用该属性的get访问器以读取该属性的值。classPerson{privatedoublesalary;publicdoubleSalary{get{returnsalary;}}}【示例8-4】演示的是调用get访问器读取属性值。get访问器的访问器体中的所有return语句都必须指定一个可隐式转换为属性类型的表达式,否则会提示错误。get访问器可用于返回字段值,或用于计算并返回字段值。【示例8-5】演示的是用get访问器计算并返回字段值。但是要注意的是通过使用get访问器更改对象的状态不是一种好的编程风格,在下面的例子中,访问器在每次访问number字段时都会产生更改对象状态的副作用。【示例8-6】演示的是使用get访问器改变了对象状态。get访问器的目的是要访问私有字段,但是它实际上的功能却很多,如可以实现方法的调用,字段的赋值及返回等。【示例8-7】演示的是get访问器的功能。3.set访问器set访问器类似于返回类型为void的方法。它使用称为value的隐式参数,此参数的类型是属性的类型。在下面的示例中,将set访问器添加到Name属性。当对属性赋值时,用提供新值的参数调用set访问器。classPerson{privateintnumber;publicintNumber{get{returnnumber;}set{number=value;}}}【示例8-8】演示的是调用set访问器的方法。set访问器中默认应该有一个参数value,但是当缺少value而将一个具体值直接赋给字段,同时调用属性时也进行了赋值时,系统将会怎样判断和执行呢?【示例8-9】演示的是set访问器中的特殊情况。从这个例子中可以看出,缺少value,系统并没有报错,但也没有采用调用属性时所赋的值,因为没有value参数去接收。8.1.4属性分类根据get访问器和set访问器是否存在,属性可按下列规则分类,如下图所示。将只读属性作为赋值目标会导致编译时错误。【示例8-10】演示的是将只读属性作为赋值目标会导致错误。除非是作为赋值的目标,否则在表达式中引用只写属性会导致编译时错误。【示例8-11】演示的是在表达式中引用只读属性会导致错误。需要注意的是在一个属性中,最多只能包含一个get访问器和一个set访问器,否则会提示错误。属性读写属性只读属性只写属性同时包含get访问器和set访问器只具有get访问器的属性只具有set访问器的属性8.2属性使用了解了属性的声明、结构和分类后就要使用属性了,因此现在我们来看看属性应该如何使用。8.2.1属性继承同字段和方法一样,属性也可以继承,在派生类中可以通过实例化对象去访问基类的属性。【示例8-12】演示的是在派生类中访问基类的属性。当在一个派生类中用与某个所继承的属性相同的名称声明一个新属性时,该派生属性将会同时在读取和写入方面隐藏所继承的属性。【示例8-13】演示的是基类属性被隐藏。发生上述情况后,如果想访问被隐藏的基类属性则应该使用强制转换来进行。【示例8-14】演示的是访问被隐藏的属性的方法。8.2.2对访问器使用访问修饰符默认情况下,set和get访问器具有与它们所属的属性相同的可访问性。例如,如果将属性设置为public,则set和get访问器也默认为public类型。然而有时,编程人员希望任何人都能获得属性的值,但是只允许其所在类的成员设置该属性,这时就需要为两种访问器设置它们自己的访问修饰符,如protected。【示例8-15】演示的是设置访问器的可访问性。在这个例子中,salary可以直接获取到,但是,并不可以随意对salary赋值,这就与我们的现实生活比较接近。在为两个访问器定义互不相同的可访问性时,必须遵守以下规则:定义时只能改变一个访问器的可访问性。例如在上面的例子中,当为get访问器也标上private修饰符时,则系统会报出如下图所示的错误。访问器的访问修饰符所指定的可访问性在限制程度上必须大于属性的可访问性。在示例8-15的例子中,如果将属性设置为private,而将get访问器设置为public,系统会提示出如示例8-16所示的错误。【示例8-16】演示的是访问修饰符的级别。8.2.3自动实现属性自动实现属性是一种非常简单的属性,它并不需要显示地定义由该属性管理的字段,而是可以让编译器自动提供底层字段,它的通用格式如下图所示。下面是如何使用自动实现属性声明UserCount属性的代码:publicintUserCount{get;set;}注意这儿没有显式地声明字段,因为编译器会自动生成保存值的匿名字段。该字段没有命名,不能直接使用,只能通过属性进行访问。typename{get;set;}属性类型属性名称存取器没有程序体【示例8-17】演示的是使用自动实现属性。这个例子中声明了三个自动实现属性,客户端代码可在对象创建后更改对象中的值。与普通属性不同的是,自动实现属性不能是只读或只写的属性,在任何情况下都必须同时指定get和set访问器。如果将示例8-17中的第一个属性改成如下所示。publicdoubleTotalPurchases{get;}上面的代码将会出现如下图所示的错误提示。虽然自动实现属性带来了一定的便利性,但是不可以直接访问备份字段,这意味着无法限制自动实现属性的值,因此只有在不需要控制备份字段的获取或设置时才使用这种属性。8.2.4对属性使用对象初始化器在示例8-17所示的例子中,字段的初始化以及属性的调用都是通过构造函数实现的,属性可以获得初始值。第三章曾介绍过使用对象初始化器去初始化字段,同样道理,属性的初始化也可以通过对象初始化器去实现。对属性使用对象初始化器的格式与字段是相同的。【示例8-18】演示的是对属性使用对象初始化器。该程序通过使用对象初始化器表达式设置了属性Count和@class的值。8.3索引器前面介绍的属性在对于一个字段进行访问和控制时很方便,但有的时候我们需要处理的是含有多个值的一个数据项。这就需要用到索引器,我们接下来的几节就来一起学习这方面的知识。8.3.1索引器的声明索引器声明类似于属性,但其功能与属性并不相同。索引器提供一种特殊的方法编写get和set访问器,可以使用户像访问数组一样访问类成员。索引器的声明格式如下图所示。在示例8-19所示的程序中声明了一个索引器,它可以访问数组arr中的每一个元素,其中的get访问器和set访问器是一样的。注意到索引器前同样可以加上访问修饰符。数据类型this[索引类型index]{get{//get访问器代码}set{//set访问器代码}}表示将要存取的数组元素类型表示索引器使用哪一类型的索引来存取数组元素【示例8-19】演示的是一维索引器的声明方法。继续观察这个程序可以发现,get访问器中,首先进行了索引范围的检查,是为了避免数组越界引发异常。同样set访问器中也进行了检查,只有确保不越界才会进行赋值。8.3.2.一维索引器的使用通过索引器可以存取类的实例的数组成员,操作方法和数组相似,一般形式如下图所示。示例8-19中的索引器进行使用应该如示例8-20所示。先声明一个对象z,在通过索引来引用该对象中的元素,从而实现为元素赋值。【示例8-20】演示的是索引器的使用方法。(同示例8-19)需要注意的是赋值时,应保持类型的一致,或者可以相互转换,否则会报错。对象名[索引]8.3.3.重载索引器索引器能够进行重载。程序将选择实参类型与作为索引的形参类型最匹配的索引器版本来执行。下面给出了一个索引器重载的例子。【示例8-21】演示的是索引器的重载方法。在这个例子中,首先声明了两个不同型的数组,继而声明了一个long型属性并进行了重载。在调用时根据索引值类型和所附的值匹配调用了两个不同属性。8.3.4.比较属性和索引器索引器与属性都是类的成员,语法上非常相似。索引器一般用在自定义的集合类中,通过使用索引器来操作对象就如同使用数组一样简单;而属性可用于任何自定义类,它增强了类的字段成员的灵活性。它们具体的不同如下图所示。属性索引器允许调用方法,如同公共数据成员允许调用对象上的方法,如同对象是一个数组其set访问器包含隐式value参数除了value参数外,其set访问器还具有与索引器相同的形参表其get访问器没有参数其get访问器具有与索引器相同的形参表可以为静态成员或实例成员必须为实例成员可通过简单的名称进行访问可通过索引器进行访问8.3.5.比较索引器和数组
本文标题:轻松学C#之属性
链接地址:https://www.777doc.com/doc-3368908 .html