用对象思考.ppt

上传人:sccc 文档编号:5638674 上传时间:2023-08-05 格式:PPT 页数:38 大小:552.54KB
返回 下载 相关 举报
用对象思考.ppt_第1页
第1页 / 共38页
用对象思考.ppt_第2页
第2页 / 共38页
用对象思考.ppt_第3页
第3页 / 共38页
用对象思考.ppt_第4页
第4页 / 共38页
用对象思考.ppt_第5页
第5页 / 共38页
点击查看更多>>
资源描述

《用对象思考.ppt》由会员分享,可在线阅读,更多相关《用对象思考.ppt(38页珍藏版)》请在三一办公上搜索。

1、第7章 用对象思考,在前一章,学习了.NET中的类和对象,也会用面向对象的思想来编写程序了。在面向对象编程中经常会遇到对象与对象之间消息的传递。其实在现实世界中我们发现对象之间存在更多的关系,如亲人关系、朋友关系等。在OOP中,同样需要对象之间建立关系,实现更多通信。本章将深入OOP编程,深入学习面向对象编程的更多特性。本章主要涉及到的知识点有:继承:实现代码的复用,建立类的层次关系。多态:理解多态原理,用抽象类和抽象方法实现多态。接口:理解接口的定义以及应用。了解序列化和反序列化,程序集和反射。了解C#面向对象特性,学会使用索引、委托和事件。,7.1 用对象思考:继承,现实中存在着各种继承关

2、系,比如动物与猫的关系、轿车与车的关系。在面向对象编程中也存在“继承”的概念,C#能够利用继承的强大机制,在面向对象编程中更好的模拟现实世界,实现更复杂的面向对象描述,实现软件中的代码复用。,7.1.1 解决代码的冗余,【本节示例参考:源代码chapter77.1.1InheritExample】在完成一个信息管理系统的时候,笔者把系统的使用者分为两类,一般用户和管理员,他们具有不同的权限。通过类图对比一下这两个类,如图7.1所示。可以看到,User类和Admin类都具有相同的属性Id和Name,具有相同的方法SayHi。也就是说对于系统的使用者,两个类都具有一些相同的状态和行为。考虑到系统的

3、扩展性,以后系统的使用者角色可能会更多,这会编写大量重复的代码,造成冗余。为了解决这个问题,下面重新创建一个新类Person,用于保存User类和Admin类公共的属性和方法,如图7.2所示。实现三个类的关系的程序如代码7-1所示。,7.1.2 建立继承模型,【本节示例参考:源代码chapter7 7.1.2InheritExample】在面向对象编程中,什么是继承呢?在现实世界中,继承关系是到处存在的,如用户和管理员都是人,他们都具有人的共同特性;猫和狗都是动物,都具有动物的共同特性。我们把现实中可以通过“is a”描述的关系,称作继承关系。即“管理员是人(Admin is a Person

4、)”,“狗是动物(dog is animal)”。C#中是如何实现继承的呢?通过“:”运算符。在声明类时,在类名称后放置一个冒号,然后在冒号后指定要从中继承的类(即基类)。例如下面的定义的类实现了“管理员是人”的关系:public class Person/Person类成员public class Admin:Person/Admin类成员,7.1.3 继承隐私保护,【本节示例参考:源代码chapter7 7.1.3InheritExample】继承可以实现不同范围的隐私保护,通过public、private、protected访问修饰符可以实现。它们的区别如图7.3所示。下面通过提出问题,

5、解决问题的方式学习继承的访问控制。(1)如果父类中的成员,只允许父类自己所独有,不允许子类和其他类访问,如何实现?将父亲的成员声明为private的,该成员就只能被父类本身访问,如图7.4所示。(2)如果父类中的成员,只允许父类和其子类访问,不允许其他类访问,如何实现将父亲的成员声明为protected的,该成员就只能被父类和其子类访问,如图7.5和图7.6所示。,7.1.4 继承的特性,继承实现了类与类之间的层次关系,描述了更加丰富多彩的面向对象世界。同时,继承也具有一些特点需要了解。(1)传递性在信息管理系统中,用户分为一般用户和管理员,因为后台管理的需要,必须要有一个超级管理员的角色来实

6、现整个系统的管理。这样子,系统的用户关系图如图7.7所示。在程序中的继承关系如图7.8所示。通过这种传递的关系,SuperAdmin不仅可以共享父类Admin的公共属性和方法,同时可以共享Person类的属性和方法。(2)单根性在上一节已经提到了,C#中的继承是单继承的,不支持多继承。(3)密封性在C#中可以通过关键字sealed来修饰一个类,这样的类是不能被继承的,这种类称为密封类。,7.1.5 继承的价值,【本节示例参考:源代码chapter77.1.5InheritExample】继承模拟了现实世界中的关系,在OOP中强调一切皆对象,这符合面向对象的编程思想,同时,继承实现了代码的重用,

7、减轻了程序员编写代码的负担。继承使程序的结构更加清晰,使类与类之间的关系层次机构化,更易于维护。合理地使用继承,会对程序编写带来很大帮助。最后,通过一个综合例子来完成对继承的学习。请看下面的描述:狗是哺乳动物。狗通常的时候和人打招呼会通过摇摇尾巴,在被抚摸感到舒服的时候,会旺旺叫,而在受到惊吓情绪烦躁时,会发出呜呜声。青蛙不是哺乳动物,属于卵生。当青蛙情绪好的时候,会在岸边呱呱呱的唱歌,而在受到惊吓时,会扑通一声跳入水中。,7.2 用对象思考:多态,在前面,学习了面向对象的封装和继承特性,通过封装保证了数据的安全性,通过继承实现了代码的重用。面向对象还有个非常重要的概念,那就是多态。本章将通过

8、多个例子来详细讲解多态的概念。,7.2.1 巧妙解决继承带来的问题,【本节示例参考:源代码chapter77.2.1AbstractExample】在管理系统中,用户和管理员都需要通过登录才能进入系统,按照继承的原理,应该将登录的公共方法放到父类当中,如图7.10所示。,7.2.2 用抽象类和抽象方法实现多态,【本节示例参考:源代码chapter77.2.1MultistateExample】上一节就是用抽象类和抽象方法实现了多态,当用户和管理员调用Login()方法的时候,他们的响应是不一样的,也就是说用户和管理员具有多态性。在基类Person中声明抽象方法Login(),让它约束子类,必须

9、实现不同的Login(),从而实现多态。(1)抽象方法是一个没有方法体的方法,用关键字abstract修饰。语法如下:abstract 返回类型 方法名();方法被标记为抽象的,要求子类必须实现该方法;方法没有方法体(方法名括号后直接跟一个分号结束),提醒编译器注意,如果子类没有重写该抽象方法,就会报错。注意:抽象方法没有闭合的大括号,下面的声明方式是错误的:abstract 返回类型 方法名()(2)含有抽象方法的类被称为抽象类,用关键字abstract修饰。语法如下:abstract class 类名抽象类中并不一定都是抽象方法,抽象类中也可以容纳具体实现的方法。但是,有抽象方法的类必然是

10、抽象类。(3)在子类中,通过关键字override重写抽象方法,语法如下:override返回类型 方法名(),7.2.3 使用虚方法实现多态,【本节示例参考:源代码chapter77.2.3VirtualExample】继续上一节的实例,在用户信息管理系统中,父类(Person)定义一个登录的抽象方法Login()。要求子类去实现它。但是通常情况下,许多角色的用户登录系统的逻辑是一样的,只是管理员(Admin)的有所差别。那么这个通用的登录方法应该放在哪里呢?按照上面的思想,应该放在父类里面。我们希望这样实现:“父类(Person)中的Login()方法实现通用的登录逻辑,一般用户(User

11、)默认采用父类的登录方法即可,管理员(Admin)可以把父类的登录方法重写,实现管理员特有的登录逻辑”。可以实现吗?C#中用virtual方法修饰的方法称为虚方法,虚方法可以有具体的实现,也可以被重写。在父类中定义虚方法,然后在子类中可以重写虚方法,也实现了面向对象的多态。虚方法的语法如下:virtual 返回类型 方法名()/方法体,7.2.4 面向对象的三大特性,到此为止,我们已经学习了面向对象中的三个重要特性:封装、继承、多态。现总结一下:(1)封装:保证对象自身数据的完整性、安全性。(2)继承:建立类之间的关系,实现代码重用,方便系统扩展。(3)多态:相同的方法调用可实现不同的实现方式

12、。,7.3 用对象思考:接口,在面向对象的编程系统中,系统的各种功能都是由许许多多的对象协作完成的。各个对象之间的协作关系成为了系统设计的关键,现在非常流行的面向接口编程实现的就是这种思想。本章将学习面向对象的接口技术。,7.3.1 接口的提出,面向对象中的接口概念,同样源于现实世界。在现实世界中,接口就是一套规范,满足这个规范的设备,就可以将它们组装到一起,从而实现设备的功能。例如,国际上定义了许多螺丝螺母的标准,根据这些标准就可以生产各种型号的螺母以及配套的螺丝,如图7.11、图7.12所示。满足标准的螺丝和螺母,可以用于制造机床、改装汽车等,只要它们提供通用的接口。,7.3.2 接口的定

13、义,【本节示例参考:源代码chapter77.3.2InterfaceExample】在.NET中,接口同样是一种标准和规范,它可以约束类的行为,使不同的类能达到一个统一的规范。接口中可以包含字段、属性、方法和索引器等。但是接口中的属性和方法都不能实现。这好比是更加抽象的抽象类。接口用interface关键字声明,语法如下:public interface IPerson int Name/接口中的属性 get;set;void SayHi();/接口中的方法,7.3.3 接口的实现,【本节示例参考:源代码chapter77.3.3InterfaceExample】定义好接口以后,类就可以实现

14、接口,实现接口的语法如下:class User:IPerson如果一个类要继承一个父类,同时要实现多个接口,则以“,”号隔开,语法如下:class User:Person,IPerson,IComparable下面,通过程序实例来学习接口。在用户管理信息系统中,要求每个用户登录,都要把登录的状态、时间写进日志。现定义一个接口ILog来完成写日志的操作,然后在User类和Admin类中分别实现这个接口。类之间的关系如图7.16所示。,7.4 老鼠的儿子会打洞,在7.2.1节,遗留了一个问题,泛型集合List用于保存用户对象,为什么父类类型约束的泛型集合可以存储它的子类对象呢?原则上子类对象是可以

15、赋给父类对象的,即子类可以完全替代父类,并且出现在父类能够出现的地方。但是反过来,父类对象是不能替换子类对象的。我们称这样的特性为“里氏替换原则(LSP)”,可以通俗地理解为“老鼠的儿子会打洞”。对于里氏替换原则有两个关键的技术,就是is操作符和as操作符。,7.4.1 is操作符,【本节示例参考:源代码chapter77.4.1IsExample】is操作符用于检查对象是否与给定类型兼容,is英文单词的意思为“是”。可以用is来判断对象是否是给定的数据类型。例如下面的语法:if(obj is Person)/执行相应的操作定义下面这样一个程序结构,然后通过代码7-10,检查程序的结果。abs

16、tract class Person/代码省略class User:Person/代码省略,7.4.2 as操作符,【本节示例参考:源代码chapter77.4.2AsExample】在讲as操作符之前,先了解一下强制类型转换。强制类型转换是通过“()”运算符来实现的。如果转换不通过,则会引发异常。如图7.17演示了强制类型转换的用法。,7.5 序列化与反序列化,序列化是将对象的状态存储到特定存储介质中的过程,也可以说是将对象状态转换为可保持或传输的过程。当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个对象转换为

17、字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为对象。,7.5.1 序列化和反序列化的用途,对于一个Windows程序来说,配置文件是非常重要的,配置文件保存了系统的设置,用户可以通过采用修改配置信息的方式定制软件的特性和功能。如图7.18显示了Windows操作系统的壁纸显示设置。当设置好壁纸的显示信息以后,程序会根据用户的要求来显示壁纸。,7.5.2 序列化和反序列化操作,【本节示例参考:源代码chapter77.5.BinarySerializerExample】.NET提供多种形式的序列化,文本、XML流等。目前使用二进制流方式对泛型支持得最好。二进制格式化器具有一个非常重要

18、的方法:Serialize()。语法如下:public void Serizlize(String serializationStream,Object graph);其中第一个参数指定序列化过程的文件流,第二个参数指定要保存的对象。序列化的基本操作流程如图7.19所示。,7.6 程序集和反射,程序集是.NET框架应用程序的主要构造块。它是一个功能集合,并以实现单个单元的形式生成、版本化和部署。.NET的应用程序由程序集(Assembly)、模块(Module)、类型(Class)几个部分组成,而反射提供一种编程的方式,让程序员可以在程序运行期获得这几个组成部分的相关信息。本章将介绍程序集和反

19、射的原理和应用。,7.6.1 什么是程序集,我们创建的控制台应用程序,在binDebug文件夹下都会生成一个.exe的文件,当需要运行程序的时候,双击.exe文件,程序会自动打开,实现所有功能。为什么运行这个文件就能实现这个功能,而无须打开开发环境呢?其实,这个编译好了的.exe文件就是程序集文件。程序集是.NET框架应用程序的生成块,它包含编译好的代码逻辑单元。程序集由描述它的清单、类型元数据、MSIL代码和资源组成,这些部分都分布在一个文件中,或者分布在多个文件中。一个项目对应一个程序集。项目名与程序集名相同。,7.6.2 查看程序集,程序生成的程序集如.exe、.dll文件等,是一个物理

20、的逻辑单元。知道了程序集结构以后,如何查看程序集呢?.NET提供了一个简易的反编译工具ILDasm,专门用于查看IL汇编语言,也可以看到程序集的类以及类的成员等。通过下面的步骤查看程序集。(1)单击“开始”“程序”“Microsoft Visual Studio 2008”“Visual Studio Tools”“Visual Studio 2008命令提示”命令。打开“命令提示”对话框。(2)在对话框中输入“ILDasm”,弹出“ILDasm程序”窗口,选择“打开”命令,打开一个.exe或者.dll文件,就能够将程序集中的内容显示出来,如图7.20所示。打开程序集清单,就可以看到版本号。打

21、开类的方法,就可以查看MSIL代码,如图7.21所示,为打开一个类的方法后展示的程序清单。同时,右击“.exe”格式的文件,也可以查看到相应的程序集属性信息,如图7.22所示。,7.6.3 反射,【本节示例参考:源代码chapter77.6.3RefectionExample】反射在编程中经常用到,例如当在Visual Studio中输入一个类型,然后输入“.”运算符时,程序就会弹出一个列表,列出这个类型所能访问的属性、方法、事件等,这就是反射机制。反射可以获取已加载的程序集和在其中定义的类型信息。也可以通过反射在运行时创建类型实例,以及调用和访问这些实例。如上一节所示,通过ILDasm查看到

22、的程序集信息,就是使用的反射机制。反射的一个主要功能就是查找程序集信息。反射主要通过下面的类实现。System.Refection.Assembly类可以获得正在运行的装配件信息,也可以动态地加载装配件,以及在装配件中查找类型信息,并创建该类型的实例。System.Refection.Type类可以获得对象的类型信息,此信息包含对象的所有要素:方法、构造器、属性等,通过Type类可以得到这些要素的信息,并且调用之。System.Refection.MethodInfo包含方法的信息,通过这个类可以得到方法的名称、参数、返回值等,并且可以调用之。,7.7 像引用数组一样引用自己的类:索引器,索引

23、器允许类或结构的实例按照与数组相同的方式进行索引。索引器是C#引入的特性,同类型的语言如Java和C+都没有这种特性。索引类似于属性,不同之处在于索引器采用参数。本章将介绍索引的定义和应用。,7.7.1 索引器的定义,【本节示例参考:源代码chapter77.7.1IndexExample】C#提供了一个非常有用的工具,就是索引器。大家都知道通过数组可以保存一系列具有相同数据类型的数据,数组对这一系列数据的操作提供了很好的支持。那么,在类中,可不可以也像引用数组一样引用自己的类呢?正好C#提供的索引器可以实现这一功能。那么,如何定义一个索引器呢?索引器采用访问修饰符后跟返回值类型、this关键

24、字、索引参数。其中索引参数包含在操作符“”中。,7.7.2 索引器的实例,【本节示例参考:源代码chapter77.7.2IndexTest】最后,通过一个实例来学习索引器的应用。该实例通过索引器来访问User类的对象。,7.8 委托和事件,C#的一个重要特性之一是支持委托(Delegate)和事件(Event)。委托和事件这两个概念是完全配合的。委托可以理解为函数指针,那就是说,它能够引用函数,通过传递地址的机制完成。事件借助委托的帮助,使用委托调用已订阅事件的对象中的方法。,7.8.1 委托的定义,在定义方法的时候,我们可以将基本数据类型作为方法参数,也可以将类实例化的对象作为方法参数传递

25、,那能不能将方法当作参数传递呢?C#提供的委托就可以实现这个功能。委托类似于C语言中的函数指针,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递。使用委托可以将多个方法绑定到同一个委托变量,当调用此变量时,可以依次调用所有绑定的方法。使用委托需要执行以下步骤。(1)定义委托(2)实例化委托(3)调用委托定义委托跟定义方法类似,只是没有方法体,语法如下:public delegate void Login(string name,string password);,7.8.2 实例化委托,【本节示例参考:源代码chapter77.8.2DelegateExample】委托定义好以

26、后,下面就可以实例化委托了,实例化委托意味着引用某个方法。要实例化委托,就要调用该委托的构造函数,并将与该委托关联的方法作为其参数传递。,7.8.3 调用委托,【本节示例参考:源代码chapter77.8.3DelegateExample】调用委托即是用委托对方法进行实例化,调用委托与调用方法类似,惟一的区别在于不是调用委托的实现,而是调用委托关联的方法的实现。,7.8.4 事件的定义,C#使用delegate和event关键字提供了一个简洁的事件处理方案。C#中的事件允许一个对象将发生的事件通知其他对象。将发生的事件通知给其他对象的对象称为发行者,对象可以订阅事件,该对象称为订阅者。一个事件

27、可以有一个或者多个订阅者,事件的发行者也可以是该事件的订阅者。举个例子,田径赛场上的运动员都准备好了,观众都聚精会神地等着裁判员的哨声。裁判员预备,吹哨,所有运动员如万箭齐发。这里的裁判员就是事件源,称为发行者。运动员都会对这个事件产生响应,是事件订阅者,而观众就没有订阅该事件,也不会参加赛跑。C#中的事件处理主要有以下几个步骤。(1)定义事件(2)订阅事件(3)触发事件,7.8.5 订阅事件,订阅事件只是添加了一个委托,当引发事件时,该委托将调用一个方法。订阅事件的语法如下:eventTest+=new delegateTest(obj1.Method);/对象obj1订阅事件eventTe

28、st+=new delegateTest(obj2.Method);/对象obj2订阅事件代码中,obj1和obj2两个对象分别订阅了事件eventTest。当事件eventTest被引发时,则会执行对象obj1的名为Method的方法,以及对象obj2的名为Method的方法。技巧:在Visual Studio 2008开发环境中,可以通过快捷方式来订阅事件:输入“事件名+=”,这时候程序会弹出提示,按一下“Tab”键,创建委托;这时候,光标会移动到委托调用的参数上,再按一下“Tab”键,程序将自动生成委托调用的函数。,7.8.6 引发事件,【本节示例参考:源代码chapter77.8.6E

29、ventExample】引发事件和调用方法类似,如下面的代码所示。if(condition)/引发事件 eventTest();引发事件eventTest时,将调用订阅此特定事件的对象的所有委托,如果没有对象订阅该事件,则事件引发时,会发生异常。下面通过一个完整的例子来总结委托和事件两部分知识。,7.9 小结,通过本章,我们学习了以下内容。(1)了解面向对象封装、继承、多态三大特性。封装:保证对象自身数据的完整性、安全性。继承:建立类之间的关系,实现代码重用,方便系统扩展。多态:相同的方法调用可实现不同的实现方式。(2)接口可以约束类的行为,使不同的类能达到一个统一的规范。接口是对类的更深一层

30、抽象。(3)里氏替换原则:原则上子类对象是可以赋给父类对象的,即子类可以完全替代父类并且出现在父类能够出现的地方。但是反过来,父类对象是不能替换子类对象的。(4)把对象转换为字节序列的过程称为对象的序列化。把字节序列恢复为对象的过程称为对象的反序列化。(5)程序集是.NET框架应用程序的生成块,它包含编译好的代码逻辑单元。反射可以获取已加载的程序集和在其中定义的类型信息。(6)索引器允许类或结构的实例按照与数组相同的方式进行索引。索引类似于属性,不同之处在于索引器采用参数。(7)委托定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递。事件允许一个对象将发生的事件通知其他对象。,本章

31、习题,一、选择题1在面向对象编程中,子类继承父类,下面说法错误的是()A子类继承父类,也可以说父类派生自一个子类B子类可以继承父类的所有成员C一个子类不能继承多个父类D父类还可以成为其他类的子类2下面关于base关键字的说法,错误的是()A在子类中,base关键字可以访问父类的属性B在子类中,base关键字不可以调用父类的构造方法C在子类中,base关键字不可以调用父类的私有方法D在子类中,base关键字不可以访问父类的私有字段3下C#中,下列代码中可以用于类型转换的是()APerson is StudentBPerson as StudentCStudent is PersonD(Student)Person二、简答题1简述面向对象的三大特性,并简述每个特性的基本功能。2简述抽象方法与虚方法的区别。,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 建筑/施工/环境 > 农业报告


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号