第5章面向对象设计•面向对象设计是软件设计的一种方法。该方法是根据面向对象思想,以对象为基础来进行软件的设计。面向对象的设计方法主要应用于大型的软件项目中。•其主要的优点是提高开发效率、节约成本、容易维护。•本章将详细讨论如何在C#中声明类、接口以及其属性和方法的面向对象技术。5.1面向对象(ObjectOriented)概述•面向对象是一种模块化的、以对象为基础的设计思想,现在被广泛应用于软件设计领域。本节将讲述面向对象的基本概念,以及如何使用面向对象的思想来设计。5.1.1对象的概念•在面向对象思想中,最基本的单元就是对象。•对象可以代表任何事物,从个人到整个学校,一个整数到一个数据集合,一滴水到一条大河等,这些都可以看作是一个对象。对象不仅表示有形的实体,也可以表示无形的、抽象的事物,如课程、计划等。术语:面向对象(Object-Oriented)面向对象编程(Object-OrientedProgramming)5.1.2面向对象的设计方法•面向对象方法(Object-OrientedMethod)是把面向对象的思想应用于软件的开发中。从程序的角度来看,对象则是被封装起来的代码块,或者称为一个功能模块。•在对象中,包含着若干个属性和方法。•类是对象的抽象,对象则是类的一个实例。例如,如果把某个苹果看作是一个对象,则这个苹果可以抽象为一个水果类,这个苹果也是水果类的实例化。•面向对象的主要特征(3个):封装性、继承性和多态性。对象的三大特征•封装:是指把相关联的属性和方法封装为统一的整体,对外只提供访问该对象的信息。使用者不必了解其内部方法的具体实现。•继承:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”。•多态:是指不同对象在接收同一个消息时产生的不同动作。多态性依赖于继承性。5.2命名空间•命名空间是用来组织类的。通常可以把相关联的类放在一个命名空间中,进行有效的管理。下面的命名空间有System(系统提供)、HelloWorld等。usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;namespaceHelloWorld{classProgram{staticvoidMain(string[]args){//程序代码}}}5.2.1命名空间的概念•常用的系统命名空间:5.2.2命名空间的定义和引用•命名空间的定义:namespaceHelloWorld{//程序代码}•命名空间的引用:利用using关键字,后跟命名空间的名称。例如:usingCustomNamespace1;5.3类•类是面向对象中最为重要的概念之一,是面向对象设计中最基本的组成模块。类可以看作一种数据结构。本节讲述类的概念、如何声明类、类的成员等。•5.3.1类的概念•在现实生活中关于类的例子:例如,学生可以看作是一个类,那么某个同学就是这个学生类的一个实例化对象。同样,老师也可以看作是一个类,某个老师是老师类的一个实例化对象。•从软件设计的角度来说,类就是一种数据结构,用于模拟现实中存在的对象和关系,包含静态的属性和动态的方法。5.3.2类的声明•C#中类的声明需要使用class关键字,并把类的主体放在花括号中,例:•publicclassStudent•{•//属性•publicstringstrName;//姓名•publicintnAge;//年龄•//方法•publicvoidGrow()•{•this.nAge++;•}•}5.3.2类的声明•格式:•1.[class-modifiers]classclass-name•2.{•3.//属性•4.//方法•5.}5.3.3类的成员和访问控制•从类的继承关系上讲,类的成员可以分为两大类:类本身声明的和从基类继承的。•类的成员类型有:常量、变量、方法、属性、事件、索引指示器、操作符以及构造函数和析构函数。•从类的访问角度来讲,类的成员又可以分为四类:–公有成员(public):允许外部访问。–私有成员(private):限定类内成员访问。–保护成员(protected):外部不能访问,子类可以访问。–内部成员(internal):同一命名空间中的类可以访问(比私有成员的限制松一些)。5.3.4构造函数和析构函数•构造函数主要的作用是执行类的实例的初始化。析构函数主要的作用就是回收系统占用的资源。•1.构造函数在实例化对象的时候,对象的初始化是自动完成的,并且这个对象是空的。有时候,希望每创造一个对象时都为其初始化某些特征,这时就需要用到构造函数。•2.析构函数上面介绍了使用构造函数在实例化对象时自动完成了一些初始化工作。反过来,在销毁对象的时候,有时候也希望能自动完成一些“收尾”任务。例如,关闭数据库连接等,C#使用析构函数来完成这个功能。(见P69)5.4字段和属性•字段和属性都提供了保存类的实例的数据信息的方法。•字段就是表示对象或类相关联的变量。主要作用是保存数据信息。(也相当于以往编程中的变量)•属性则实现了数据的封装和隐藏,可以看作是实体特征的抽象表现。需要用到set和get方法。•用途:在实际项目的开发过程中,公共字段和属性在合适的条件下都可以使用,但是我们应该尽可能的使用属性(property),而不是字段(field)。把所有的字段都设置为私有字段,如果要暴露它们,则把它们封装成属性,这也是微软推荐的方式。5.4.1字段•字段可以简单地理解为成员变量,其主要作用是保存数据信息。字体的修饰符可以是以下几种:new,public,internal,protected,static和readonly。例如,下面的代码段就是在类Class1中,声明了3个域。代码如下:1.classClass12.{3.publicinta;4.protectedstringb;5.privatedecimalc;6.}5.4.2属性•属性可以看作是实体特征的抽象表现。例如,一个学生的姓名、一个软件的大小、一个班级的人数等,这些都可以作为属性来表示。属性的声明涉及两个关键字:get(读操作)和set(写操作)。例:privateintstudentCount;//内部字段publicintStudentCount{//声明属性StudentCountget{//读属性的值returnstudentCount;}set{//设置属性的值studentCount=value;}}5.5方法•方法属于类的成员之一,用于执行计算或者其他的行为。在本节中将讲述如何声明方法、方法的参数以及方法的重载等。例:///学生增长一岁publicvoidGrow(){this.nAge++;}///求圆形面积publicdoubleGetArea(){returnMath.PI*r*r;}5.5.2参数•利用ref关键字进行引用传递:publicvoidGrow(int_nAgeSpan,refint_nOutCurrentAge)效果:相当于C语言的传址&。注意:_nOutCurrentAge必须初始化。•使用out关键字传出参数值:publicvoidGrow(int_nAgeSpan,outint_nOutCurrentAge)_nOutCurrentAge无需初始化。•使用params关键字传递多个参数值:voidSetHobby(paramsstring[]_strArrHobby)5.5.3静态方法•静态方法:表示类所具有的行为,而非其对象所具有的行为。比如,学生分班,开会。•定义:用Static关键字。///计算矩形面积。定义静态方法publicstaticvoidGetArea(double_dblWidth,double_dblHeight){Console.WriteLine(_dblHeight*_dblWidth);}Rectangle.GetArea(4,8);//调用静态方法•注意:Rectangle是类名,而非对象名。5.5.4方法的重载•有时候,对于类需要完成的同一个功能的要求可能比较复杂。例如,对学生类而言,如果想要使其具有一个“成长”方法,但是这个方法,可能使其增长一岁,也可能增加指定的岁数,该怎么解决这个问题呢?///成长1岁publicvoidGrow(){this.nAge++;}///成长_nAgeSpan岁。方法的重载。publicvoidGrow(int_nAgeSpan){this.nAge+=_nAgeSpan;}5.5.5操作符的重载•上面介绍了方法的重载,C#还提供了重载机制:允许重载运算符,如“+”,“^”等,在原来功能的基础上,完成用户自定义的功能。例如,对于复数运算,可以定义方法:Add(1+2i,2+3i)=3+5i•而使用符号看起来会更简洁:(1+2i)+(2+3i)=3+5i5.6抽象类•面向对象编程思想试图模拟现实中的对象和关系。但是,有时候,基类并不是与具体的事物相联系的,而是表达一种抽象的概念。抽象类就可以满足这种关系。本节讲述抽象类的概念和使用。5.6.1抽象类的概念•现实中,存在如图所示的对象及关系,父类“运动员”有3个子类,这3个子类都可以继承父类的“训练”这个方法,但是,仔细考虑一下,父类“运动员”的训练该如何实现呢?•类似地,形状是抽象类,圆、三角形、长方形是子类。5.6.2抽象类的声明•上面介绍了什么是抽象方法和抽象类,现在来看如何在C#中实现。•(1)在C#中,使用关键字abstract来定义抽象方法(abstractmethod),并需要把abstract关键字放在访问级别修饰符和方法返回数据类型之前,没有方法实现的部分,格式如下:publicabstractvoidTrain();•(2)子类继承抽象父类之后,可以使用override关键字覆盖父类中的抽象方法,并做具体的实现,格式如下:publicoverridevoidTrain(){…}5.6.3抽象方法•子类在继承了抽象父类之后,就可以具体实现其中的抽象方法了。3个子类运动员分别实现了抽象方法Train()。5.7接口•接口实际上是定义了一组数据结构,通过这组数据结构,可以调用组件对象的功能。接口和抽象类很相似,在本节中,除了讲解接口的概念和使用外,还会讲述接口与抽象类的区别。5.7.1接口的概念•接口和抽象类非常相似,它定义了一些未实现的属性和方法。所有继承它的类都继承这些成员,在这个角度上,可以把接口理解为一个类的模板。interfaceIShape//声明IShape接口{doubleGetArea();}publicdoubleGetArea()//求圆形面积{returnMath.PI*dblRadius*dblRadius;}publicdoubleGetArea()//求矩形面积{returnthis.dblHeight*this.dblWidth;}5.7.2接口的声明•下面通过一个具体的例子,介绍如何在C#中声明和使用接口。•示例中有一个“形状”的概念,它有3个具体的形状类:矩形、圆形、三角形。可以看出,在某种意义上,接口与抽象类非常相似。5.7.3接口的实现•声明接口之后,类就可以通过继承接口来实现其中的抽象方法。继承接口的语法同类的继承类似,使用冒号“:”,将待继承的接口放在类的后面。如果继承于多个接口,使用逗号将其分隔。•下面的代码使用Rectangle类来求一个矩形的面积:•1.Rectangler=newRectangle(3,5);•2.Console.WriteLine(r.GetArea());//输出155.7.4接口与抽象类•接口和抽象类非常相似,它定义了一些未实现的属性和方法。所有继承它的类都继承这些成员,在这个角度上,可以把接口理解为一个类的模板。相似之处:–两者都包含可以由子类继承的抽象成员;