第3章 面向对象编程基础.ppt_第1页
第3章 面向对象编程基础.ppt_第2页
第3章 面向对象编程基础.ppt_第3页
第3章 面向对象编程基础.ppt_第4页
第3章 面向对象编程基础.ppt_第5页
已阅读5页,还剩98页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第3章面向对象编程基础,3.1面向对象概念对象是面向对象编程的基本成分。什么是对象?可以说任何事物都是对象。对象可以是现实世界中的一个物理对象,也可以是抽象的概念或规则。对象可用它本身的一组属性和操作功能来定义。例如:对于自行车对象,它的颜色、车轮个数等是对象的状态,它能行驶、能变速是对象的功能;对于电视机对象,它的屏幕大小、色彩、频道数等是它的属性,能打开、关闭、更换频道、调整音量等是它的操作。,3.1.1对象、类、实例化,在面向对象程序设计技术中,对象是具有属性(又称状态)和操作(又称方法、行为方式和消息等)的实体。对象的属性表示了它所处于的状态;对象的操作则用来改变对象的状态达到特定的功能。对象有一个惟一的标识名以区别于其他对象,对象有固定的对外接口,是对象在约定好的运行框架和消息传递机制中与外界通信的通道。对象是面向对象技术的核心,是构成系统的基本单元,所有的面向对象的程序都是由对象来组成的。类是在对象之上的抽象,它为属于该类的全部对象提供了统一的抽象描述。所以类是一种抽象的数据类型,它是对象的模板,对象则是类的具体化,是类的实例。例如:“一台PANDA电视机”等价于“这是电视机类的一个实例”。类与对象的关系如图3.1所示。,3.1.1对象、类、实例化,图3.1类与对象关系,3.1.2面向对象程序设计语言的三大原则,一个面向对象的语言在处理对象时,必须遵循的三个原则是:封装、继承、多态。1.封装所谓“封装”,就是用一个框架把数据和代码组合在一起,形成一个对象。遵循面向对象数据抽象的要求,一般数据都被封装起来,也就是外部不能直接访问对象的数据,外部能见到的只有提供给外面访问的公共操作(也称接口,对象之间联系的渠道)。在C#中,类是支持对象封装的工具,对象则是封装的基本单元。封装的对象之间进行通讯的一种机制叫做消息传递。消息是向对象发出的服务请求,是面向对象系统中对象之间交互的途径。消息包含要求接收对象去执行某些活动的信息,以及完成要求所需的其他信息(参数)。发送消息的对象不需要知道接收消息的对象如何对请求予以响应。接收者接收了消息,它就承担了执行指定动作的责任,作为消息的答复,接收者将执行某个方法,来满足所接收的请求。,3.1.2面向对象程序设计语言的三大原则,2.继承继承是面向对象编程技术的一块基石,通过它可以创建分等级层次的类。例如,创建一个汽车的通用类,它定义了汽车的一般属性(如:车轮、方向盘、发动机、车门)和操作方法(如:前进、倒退、刹车、转弯等)。从这个已有的类可以通过继承的方法派生出新的子类,卡车、轿车、客车等,它们都是汽车类的更具体的类,每个具体的类还可增加自己一些特有的东西。如图3.2所示,更一般地表示如图3.3所示。,图3.2汽车类的派生,图3.3类的继承,2.继承,继承是父类和子类之间共享数据和方法的机制,通常把父类称为基类,子类称为派生类。一个基类可以有任意数目的派生类,从基类派生出的类还可以被派生,一群通过继承相联系的类就构成了类的树型层次结构。如果一个类有两个或两个以上直接基类,这样的继承结构被称为多重继承或多继承。在现实世界中这种模型屡见不鲜,如:一些组合功能的产品像沙发床,它既有沙发的功能,又有床的功能,沙发床应允许同时继承沙发和床的特征。如图3.4所示,更一般地表示如图3.5所示,图3.4多继承示意,图3.5类的多继承,2.继承,尽管多继承从形式上看比较直观,但在实现上多继承可能引起继承操作或属性的冲突。当今的很多语言已不再支持多继承,C#语言也对多继承的使用进行了限制,它通过接口来实现。接口可以从多个基接口继承。接口可以包含方法、属性、事件和索引器。一个典型的接口就是一个方法声明的列表,接口本身不提供它所定义的成员的实现。所以接口不能被实例化,一个实现接口的类再以适当的方式定义接口中声明的方法。如图3.6所示。,图3.6类的接口,3.1.2面向对象程序设计语言的三大原则,3.多态性多态性就其字面上的意思是:多种形式或多种形态。在面向对象编程中,多态是指同一个消息或操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在面向对象编程中,多态性有两种,一种是静态多态,一种是动态多态。当在同一个类中,直接调用一个对象的方法时候,系统在编译时,根据传递的参数个数、参数类型以及返回值的类型等信息决定实现何种操作,这就是所谓的静态绑定。当在一个有着继承关系的类层次结构中,间接调用一个对象的方法时候,也就是说,调用经过基类的操作,这种调用只有到系统运行时,才能根据实际情况决定实现何种操作,这就是所谓的动态绑定。C#支持这两种类型的多态,在实现多态上C#可以有几种方式:接口多态性、继承多态性、通过抽象类实现的多态性。,3.2类,正如前节所述,对象是面向对象语言的核心,数据抽象和对象封装是面向对象技术的基本要求,而实现这一切的主要手段和工具就是类。从编程语言的角度讲,类就是一种数据结构,它定义数据和操作这些数据的代码。把握面向对象编程的重要一步就是区分类与对象,类是对其成员的一种封装,对类进行对象实例化,并在其数据成员上实施操作才是完成现实任务的根本。实例化后的类为对象,其核心特征便是拥有了一份自己特有的数据成员拷贝。这些为对象所持有的数据成员称之为实例成员。不为对象所持有的数据成员称之为静态成员,在类中用static修饰符声明。类的成员包含数据成员(常量、域、事件)和函数成员(方法、属性、索引器、操作符、构造函数、析构函数等)。,3.2.1类的声明,要定义一个新的类,首先要声明它。语法形式:属性集信息opt类修饰符optclass类名:类基opt类主体opt其中:属性集信息:是C#语言提供给程序员为程序中定义的各种实体附加一些说明信息,这是C#语言的一个重要特征。类修饰符:可以是以下表3.1所列的几种之一或者是它们的有效组合,但在类声明中,同一修饰符不允许出现多次。,表3.1类的修饰符,3.2.1类的声明,类基:它定义该类的直接基类和由该类实现的接口。当多于一项时,用逗号“,”分隔。如果没有显式地指定直接基类,那么它的基类隐含为object。最简单的类声明语法形式:class类名类成员例如:classPoint/Point类的访问权限缺省为publicintx,y;,3.2.2类的成员,类的定义包括类头和类体两部分,其中类体用一对大花括号括起来,类体用于定义该类的成员。类体语法形式:类成员声明opt类成员由两部分组成,一个是类体中以类成员声明形式引入的类成员,另一个则是直接从它的基类继承而来的成员。类成员声明主要包括:常数声明、字段声明、方法声明、属性声明、事件声明、索引器声明、运算符声明、构造函数声明、析构函数声明、静态构造函数、类型声明等。当字段、方法、属性、事件、运算符和构造函数声明中含有static修饰符时,则表明它们是静态成员,否则就是实例成员。类成员声明中可以使用以下5种访问修饰符中的一种:public,private,protected,internal,protectedinternal。当类成员声明不包含访问修饰符时,缺省约定访问修饰符为private。,3.2.2类的成员,1常数声明常数声明一般语法形式:属性集信息opt常数修饰符optconst类型标识符=常数表达式,标识符=常数表达式0+其中:常数修饰符可以是:new、public、protected、internal、private。类型必须是:sbyte、byte、short、ushort、int、uint、long、ulong、char、float、double、decimal、bool、string、枚举类型或引用类型。常数表达式的值类型应与目标类型相一致,或者可以通过隐式转换转换成目标类型。2字段声明字段声明的一般语法形式:属性集信息opt字段修饰符opt类型变量声明列表;其中:变量声明列表:标识符或者用逗号“,”分隔的多个标识符,并且变量标识符还可用赋值号“=”设定初始值。,3.2.2类的成员,【例3.1】通过构造函数对只读字段赋值。usingSystem;publicclassAreapublicreadonlydoubleRadius;/Radius是只读字段privatedoublex,y;publicdoubleSize;publicstaticdoubleSum=0.0;publicArea()Radius=1.0;/通过构造函数对radius赋值classTestpublicstaticvoidMain()Areas1=newArea();Console.WriteLine(Radius=0,Size=1,Sum=2,s1.Radius,s1.Size,Area.Sum);/静态字段通过类访问Area.Sum,实例字段通过对象访问s1.SizeConsole.Read();,【例3.1】,运行结果如图3.7所示。,图3.7对只读字段赋值的运行结果,3.2.3构造函数,当定义了一个类之后,就可以通过new运算符将其实例化,产生一个对象。为了能规范、安全地使用这个对象,C#提供了对对象进行初始化的方法,这就是构造函数。在C#中,类的成员字段可以分为实例字段和静态字段,与此相应的构造函数也分为实例构造函数和静态构造函数。1实例构造函数声明实例构造函数的声明语法形式:属性集信息opt构造函数修饰符opt标识符(参数列表opt):base(参数列表opt)opt:this(参数列表opt)opt构造函数语句块其中:构造函数修饰符:public、protected、internal、private、extern。一般地,构造函数总是public类型的。如果是private类型的,表明类不能被外部类实例化。标识符(参数列表opt):标识符是构造函数名,必须与这个类同名,不声明返回类型,并且没有任何返回值。这与返回值类型为void的函数不同。构造函数参数可以没有,也可以有一个或多个。这表明构造函数在类的声明中可以有函数名相同,但参数个数不同或者参数类型不同的多种形式,这就是所谓的构造函数重载。,3.2.3构造函数,【例3.2】Time类的构造函数及其重载。usingSystem;publicclassTimeprivateinthour,minute,second;publicTime()hour=minute=second=0;publicTime(inth)hour=h;minute=second=0;publicTime(inth,intm)hour=h;minute=m;second=0;,publicTime(inth,intm,ints)hour=h;minute=m;second=s;classTeststaticvoidMain()Timet1,t2,t3,t4;/对t1,t2,t3,t4分别调用不同的构造函数t1=newTime();t2=newTime(8);t3=newTime(8,30);t4=newTime(8,30,30);,3.2.3构造函数,【例3.3】构造函数初始化。实例对象创建时,根据不同的参数调用相应的构造函数完成初始化。usingSystem;classPointpublicdoublex,y;publicPoint()x=0;y=0;publicPoint(doublex,doubley)this.x=x;/当this在实例构造函数中使用时,this.y=y;/它的值就是对该构造的对象的引用。,classTestpublicstaticvoidMain()Pointa=newPoint();Pointb=newPoint(3,4);/用构造函数初始化对象Console.WriteLine(a.x=0,a.y=1,a.x,a.y);Console.WriteLine(b.x=0,b.y=1,b.x,b.y);Console.Read();运行结果如图3.8所示。,图3.8用不同的构造函数完成的对象初始化的运行结果,3.2.3构造函数,上例中声明了一个类Point,它提供了两个重载的构造函数。一个是不带参数的Point构造函数和一个是带有两个double参数的Point构造函数。如果类中没有提供这些构造函数,那么CLR会自动提供一个缺省构造函数的。但一旦类中提供了自定义的构造函数,系统则不提供缺省构造函数,这一点需要特别注意,否则系统编译会报出错。如图3.9所示。,图3.9程序编译出错信息,3.2.3构造函数,【例3.4】Point类只定义了一个带两个参数的构造函数,在创建Point对象a时,编译会报告出错信息。usingSystem;classPointpublicdoublex,y;publicPoint(doublex,doubley)this.x=x;/当this在实例构造函数中使用时,this.y=y;/它的值就是对该构造的对象的引用。classTestpublicstaticvoidMain()Pointa=newPoint();/出错Pointb=newPoint(3,4);/用构造函数初始化对象Console.WriteLine(a.x=0,a.y=1,a.x,a.y);Console.WriteLine(b.x=0,b.y=1,b.x,b.y);,3.2.3构造函数,【例3.5】派生类构造函数及其调用。usingSystem;classPointprivateintx,y;publicPoint()x=0;y=0;Console.WriteLine(Point()constructor:0,this);publicPoint(intx,inty)this.x=x;this.y=y;Console.WriteLine(Point(x,y)constructor:0,this);,classCircle:Pointprivatedoubleradius;publicCircle()/缺省约定调用基类的无参构造函数Point()Console.WriteLine(Circle()constructor:0,this);publicCircle(doubleradius):base()this.radius=radius;Console.WriteLine(Circle(radius)constructor:0,this);publicCircle(intx,inty,doubleradius):base(x,y)this.radius=radius;Console.WriteLine(Circle(x,y,radius)constructor:0,this);,classTeststaticvoidMain()Pointa=newPoint();Circleb=newCircle(3.5);Circlec=newCircle(1,1,4.8);Console.Read();运行结果如图3.10所示。,图3.10派生类对象在初始化时,调用构造函数的次序,3.2.3构造函数,2.静态构造函数声明静态构造函数的声明语法形式:属性集信息opt静态构造函数修饰符opt标识符()静态构造函数体其中:静态构造函数修饰符:externstatic或者staticextern如果有extern修饰,则说明这是一个外部静态构造函数,不提供任何实际的实现,所以静态构造函数体仅仅是一个分号。标识符():标识符是静态构造函数名,必须与这个类同名,静态构造函数不能有参数。静态构造函数体:静态构造函数的目的是用于对静态字段进行初始化,所以它只能对静态数据成员进行初始化,而不能对非静态数据成员进行初始化。静态构造函数是不可继承的,而且不能被直接调用。只有创建类的实例或者引用类的任何静态成员时,才能激活静态构造函数,所以在给定的应用程序域中静态构造函数至多被执行一次。如果类中没有声明静态构造函数,而又包含带有初始设定的静态字段,那么编译器会自动生成一个默认的静态构造函数。,3.2.3构造函数,【例3.6】静态构造函数。usingSystem;classScreenstaticintHeight;staticintWidth;intCur_X,Cur_Y;staticScreen()/静态构造函数,对类的静态字段初始化Height=768;Width=1024;,3.2.4析构函数,一般来说,创建一个对象时需要用构造函数初始化数据,与此相对应释放一个对象时就用析构函数。所以析构函数是用于实现析构类实例所需操作的方法。声明语法形式:属性集信息optexternopt标识符()析构函数体其中:标识符必须与类名相同,但为了区分构造函数,前面需加“”表明它是析构函数。析构函数不能写返回类型,也不能带参数,因此它不可能被重载,当然它也不能被继承,所以一个类最多只能有一个析构函数。一个类如果没有显式地声明析构函数,则编译器将自动产生一个缺省默认的析构函数。析构函数不能由程序显式地调用,而是由系统在释放对象时自动调用。如果这个对象是一个派生类对象,那么在调用析构函数时也会产生链式反应,首先执行派生类的析构函数,然后执行基类的析构函数,如果这个基类还有自己的基类,这个过程就会不断重复,直到调用Object类的析构函数为止,其执行顺序正好与构造函数相反。,3.2.4析构函数,【例3.7】析构函数的调用次序。usingSystem;publicclassPointprivateintx,y;Point()Console.WriteLine(Pointsdestructor);publicclassCircle:Pointprivatedoubleradius;Circle()/缺省约定调用基类的无参构造函数Point()。Console.WriteLine(Circlesdestructor);,classTestpublicstaticvoidMain()Circleb=newCircle();b=null;GC.Collect();GC.WaitForPendingFinalizers();Console.Read();运行结果如图3.11所示。,图3.11析构函数的调用次序,3.3方法,3.3.1方法的声明方法是按照一定格式组织的一段程序代码,在类中用方法声明的方式来定义。方法声明语法形式:属性集信息opt方法修饰符opt返回类型方法名(形参表opt)方法体其中:方法修饰符有如表3.2所示。,表3.2方法的修饰符,3.3.1方法的声明,方法修饰符中public、protected、private、internal、protectedinternal属于访问修饰符,用于表示访问的级别,默认情况下,方法的访问级别为public。访问修饰符也可以和其它的方法修饰符有效地组合在一起,但某些修饰符是互相排斥的。表3.3所列的组合被视为非法。,表3.3修饰符的无效组合,3.3.1方法的声明,【例3.8】下面程序中的StackTp类定义了几个方法以模拟实现一个压栈操作。usingSystem;classStackTpintMaxSize;intTop;intStkList;publicStackTp()/构造函数MaxSize=100;Top=0;StkList=newintMaxSize;publicStackTp(intsize)/构造函数MaxSize=size;Top=0;StkList=newintMaxSize;,publicboolisEmptyStack()/方法if(Top=0)returntrue;elsereturnfalse;publicboolisFullStack()if(Top=MaxSize)returntrue;elsereturnfalse;publicvoidpush(intx)StkListTop=x;Top+;,classTestpublicstaticvoidMain()StackTpST=newStackTp(20);strings1;if(ST.isEmptyStack()/调用方法isEmptyStack()s1=Empty;elses1=notEmpty;Console.WriteLine(Stackis+s1);for(inti=0;iy)tmp=x;x=y;y=tmp;if(xz)tmp=x;x=z;z=tmp;if(yz)tmp=y;y=z;z=tmp;,classTeststaticvoidMain()Myclassm=newMyclass();inta,b,c;a=30;b=20;c=10;m.Sort(a,b,c);Console.WriteLine(a=0,b=1,c=2,a,b,c);Console.Read();运行结果如图3.13所示:,图3.13方法的值参数传递的运行结果,【例3.9】,3.3.2方法的参数,【例3.10】下面程序演示的是当方法传递的是一个引用对象(例如数组时)时,对形参的修改会影响到实参。usingSystem;classMyclasspublicvoidSortArray(inta)inti,j,pos,tmp;for(i=0;iaj)pos=j;if(pos!=i)tmp=ai;ai=apos;apos=tmp;,classTeststaticvoidMain()Myclassm=newMyclass();intscore=87,89,56,90,100,75,64,45,80,84;m.SortArray(score);for(inti=0;iy)tmp=x;x=y;y=tmp;if(xz)tmp=x;x=z;z=tmp;if(yz)tmp=y;y=z;z=tmp;,【例3.11】,classTeststaticvoidMain()Myclassm=newMyclass();inta,b,c;a=30;b=20;c=10;m.Sort(refa,refb,refc);Console.WriteLine(a=0,b=1,c=2,a,b,c);Console.Read();运行结果如图3.15所示。,图3.15引用参数传递方式的运行结果,2.引用参数,使用ref参数的注意点:(1)ref关键字仅对跟在它后面的参数有效,而不能应用于整个参数表。例如Sort方法中x,y,z都要加ref修饰。(2)在调用方法时,也用ref修饰实参变量,因为是引用参数,所以要求实参与形参的数据类型必须完全匹配,而且实参必须是变量,不能是常量或表达式。(3)在方法外,ref参数必须在调用之前明确赋值,在方法内,ref参数被视为初始值已赋过。,3.3.2方法的参数,3.输出参数在参数前加out修饰符的被称为输出参数,它与ref参数很相似,只有一点除外,就是它只能用于从方法中传出值,而不能从方法调用处接受实参数据。在方法内out参数被认为是未赋过值的,所以在方法结束之前应该对out参数赋值。【例3.12】在下面程序4-7中,求一个数组元素中的最大值、最小值以及平均值。希望得到三个返回值,显然用方法的返回值不能解决,而且这三个值必须通过计算得到,初始值没有意义,所以解决方案可以定义三个out参数。usingSystem;classMyclasspublicvoidMaxMinArray(inta,outintmax,outintmin,outdoubleavg)intsum;sum=max=min=a0;for(inti=1;imax)max=ai;if(ai=y?x:y;publicintmax(intx,inty,intz)returnmax(max(x,y),z);publicdoublemax(doublex,doubley,doublez),【例3.16】,returnmax(max(x,y),z);classTeststaticvoidMain()Myclassm=newMyclass();inta,b,c;doublee,f,g;a=10;b=20;c=30;e=1.5;f=3.5;g=5.5;/调用方法时,编译器会根据实参的类型和个数调用不同的方法。Console.WriteLine(max(0,1)=2,a,b,m.max(a,b);Console.WriteLine(max(0,1,2)=3,a,b,c,m.max(a,b,c);Console.WriteLine(max(0,1)=2,e,f,m.max(e,f);Console.WriteLine(max(0,1,2)=3,e,f,g,m.max(e,f,g);Console.Read();,【例3.16】,运行结果如图3.20所示。,图3.20重载max方法的运行结果,从上例可以看出,方法max是求若干参数中最大值,类Myclass中有四个同名的方法max,它们或参数个数不一样,或参数类型不一样。在调用max方法时,编译器会根据调用时给出的实参个数及类型调用相应的方法,这就是编译时实现的多态。多态是面向对象编程语言的特性之一,重载是多态的形式之一,在C#中,最常用的重载就是方法重载。,3.3.4方法的重载与覆盖,【例3.17】下面程序定义了一个基类Shape,含有字段域width和height分别表示形状的宽和高,并定义了一个area方法求形状的面积。它的派生类Triangle和Trapezia都用关键字new修饰了area方法。usingSystem;classShapeprotecteddoublewidth;protecteddoubleheight;publicShape()width=height=0;publicShape(doublex)width=height=x;publicShape(doublew,doubleh)width=w;height=h;publicdoublearea()returnwidth*height;,【例3.17】,classTriangle:Shape/三角形publicTriangle(doublex,doubley):base(x,y)newpublicdoublearea()/派生类方法与基类方法同名,编译时会有警告信息returnwidth*height/2;classTrapezia:Shape/梯形doublewidth2;publicTrapezia(doublew1,doublew2,doubleh):base(w1,h)width2=w2;newpublicdoublearea()/加new隐藏基类的area方法,【例3.17】,return(width+width2)*height/2;classTeststaticvoidMain()ShapeA=newShape(2,4);TriangleB=newTriangle(1,2);TrapeziaC=newTrapezia(2,3,4);Console.WriteLine(A.area=0,A.area();/调Shape的area方法Console.WriteLine(B.area=0,B.area();/调Triangle的area方法Console.WriteLine(C.area=0,C.area();/调Trapezia的area方法A=B;/在C#中,基类的引用也能够引用派生类对象Console.WriteLine(A.area=0,A.area();/调Shape的area方法A=C;Console.WriteLine(A.area=0,A.area();/调Shape的area方法Console.Read();,【例3.17】,运行结果如图3.21所示。,图3.21继承结构中的方法重载的运行结果,从例中可以看出,使用关键字new修饰方法,可以在一个继承的结构中隐藏有相同签名的方法。但是正如程序中演示的基类对象A被引用到派生类对象B时,它访问的仍是基类的方法。更多的时候,我们期望根据当前所引用的对象来判断调用哪一个方法,这个判断过程是在运行时进行的。,3.3.4方法的重载与覆盖,【例3.18】将例3.17改写,在Shape类中方法area用virtual修饰,而派生类Triangle和Trapezia用关键字override修饰area方法,这样就可以在程序运行时决定调用哪个类的area方法。usingSystem;classShapeprotecteddoublewidth;protecteddoubleheight;publicShape()width=height=0;publicShape(doublex)width=height=x;publicShape(doublew,doubleh)width=w;height=h;publicvirtualdoublearea()/基类中用virtual修饰符声明一个虚方法returnwidth*height;,【例3.18】,classTriangle:Shape/三角形publicTriangle(doublex,doubley):base(x,y)publicoverridedoublearea()/派生类中用override修饰符覆盖基类虚方法returnwidth*height/2;classTrapezia:Shape/梯形doublewidth2;publicTrapezia(doublew1,doublew2,doubleh):base(w1,h)width2=w2;publicoverridedoublearea()/派生类中用override修饰符覆盖基类虚方法,【例3.18】,return(width+width2)*height/2;classTeststaticvoidMain()ShapeA=newShape(2,4);TriangleB=newTriangle(1,2);TrapeziaC=newTrapezia(2,3,4);Console.WriteLine(A.area=0,A.area();/调Shape的area方法Console.WriteLine(B.area=0,B.area();/调Triangle的area方法Console.WriteLine(C.area=0,C.area();/调Trapezia的area方法A=B;Console.WriteLine(A.area=0,A.area();/调Triangle的area方法A=C;Console.WriteLine(A.area=0,A.area();/调Trapezia的area方法Console.Read();,【例3.18】,return(width+width2)*height/2;classTeststaticvoidMain()ShapeA=newShape(2,4);TriangleB=newTriangle(1,2);TrapeziaC=newTrapezia(2,3,4);Console.WriteLine(A.area=0,A.area();/调Shape的area方法Console.WriteLine(B.area=0,B.area();/调Triangle的area方法Console.WriteLine(C.area=0,C.area();/调Trapezia的area方法A=B;Console.WriteLine(A.area=0,A.area();/调Triangle的area方法A=C;Console.WriteLine(A.area=0,A.area();/调Trapezia的area方法Console.Read();,【例3.18】,运行结果如图3.22所示。,图3.22用virtual和override实现的方法重载的运行结果,从例中可以看到,由于area方法在基类被定义为虚方法又在派生类中被覆盖,所以当基类的对象引用A被引用到派生类对象时,调用的就是派生类覆盖的area方法。一个重载方法可以通过base引用被重载的基方法。例如:classTriangle:Shape/三角形publicoverridedoublearea()/派生类中用override修饰符覆盖基类虚方法doubles;s=base.area();/调用基类的虚方法returns/2;,3.3.4方法的重载与覆盖,在类的层次结构中,只有使用override修饰符,派生类中的方法才可以覆盖(重载)基类的虚方法,否则就是隐藏基类的方法。具体使用过程应注意以下几点:(1)不能将虚方法声明为静态的,因为多态性是针对对象的,不是针对类的。不能将虚方法声明为私有的,因为私有方法不能被派生类覆盖。(3)覆盖方法必须与它相关的虚方法匹配,也就是说,它们的方法签名(方法名称、参数个数、参数类型)、返回类型以及访问属性等都应该完全一致。(4)一个覆盖方法覆盖的必须是虚方法,但它本身又是一个隐式的虚方法,所以它的派生类还可以覆盖这个方法。不过尽管如此还是不能将一个覆盖方法显式地声明为虚方法。,3.4属性,为了实现良好的数据封装和数据隐藏,类的字段成员的访问属性一般设置成private或protected,这样在类的外部就不能直接读写这些字段成员了,通常的办法是提供public级的方法来访问私有的或受保护的字段。【例3.19】下面程序中的TextBox类提供公共方法set_text和get_text来访问私有数据text。usingSystem;classTextBoxprivatestringtext;privatestringfontname;privateintfontsize;privateboolmultiline;publicTextBox(),【例3.19】,text=text1;fontname=宋体;fontsize=12;multiline=true;publicvoidset_text(stringstr)text=str;publicstringget_text()returntext;,【例3.19】,classTeststaticvoidMain()TextBoxText1=newTextBox();Console.WriteLine(Text1.text=0,Text1.get_text();Text1.set_text(这是文本框);Console.WriteLine(Text1.text=0,Text1.get_text();Console.Read();运行结果如图3.23所示。,图3.23程序的运行结果,3.4属性,但C#提供了属性(property)这个更好的方法,把字段域和访问它们的方法相结合。对类的用户而言,属性值的读写与字段域语法相同,对编译器来说,属性值的读写是通过类中封装的特别方法get访问器和set访问器实现的。属性的声明语法形式:属性集信息opt属性修饰符opt类型成员名访问器声明其中:属性修饰符:属性修饰符与方法修饰符相同,包括new、static、virtual、abstract、override和四种访问修饰符的合法组合,它们遵循相同的规则。类型:指定该声明所引入的属性的类型。成员名:指定该属性的名称。访问器声明:声明属性的访问器,可以是一个get访问器或一个set访问器,或者两个都有。,3.4属性,访问器声明语法形式:get/读访问器/访问器语句块set/写访问器/访问器语句块get访问器的返回值类型与属性的类型相同,所以在语句块中的return语句必须有一个可隐式转换为属性类型的表达式。set访问器没有返回值,但它有一个隐式的值参数,其名称为value,value的类型与属性的类型相同。同时包含get和set访问器的属性是读写属性,只包含get访问器的属性是只读属性,只包含set访问器的属性是只写属性。,3.4属性,【例3.20】改写例3.19,对TextBox类的text、fontname、fontsize、multiline域提供属性方式的读写访问。usingSystem;classTextBoxprivatestringtext;privatestringfontname;privateintfontsize;privateboolmultiline;publicTextBox()text=text1;fontname=宋体;fontsize=12;multiline=false;,【例3.20】,publicstringText/Text属性,可读可写getreturntext;settext=value;publicstringFontName/FontName属性,只读属性getreturnfontname;publicintFontSize/FontSize属性,可读可写getreturnfontsize;setfontsize=value;,【例3.20】,publicboolMultiLine/MultiLine属性,只写setmultiline=value;classTeststaticvoidMain()TextBoxText1=newTextBox();/调用Text属性的get访问器Console.WriteLine(Text1.Text=0,Text1.Text);Text1.Text=这是文本框;/调用Text属性的set访问器Console.WriteLine(Text1.Text=0,Text1.Text);,【例3.20】,Console.WriteLine(Text1.Fontname=0,Text1.FontName);Text1.FontSize=36;Text1.MultiLine=true;Console.WriteLine(Text1.FontSize=0,Text1.FontSize);Console.Read();运行结果如图3.24所示。,图3.24程序的运行结果,3.4属性,属性是字段的自然扩展,当然属性也可作为特殊的方法使用,并不要求它和字段域一一对应,所以属性还可以用于各种控制和计算。例如:【例3.21】下面程序中定义的Label类设置了Width和Heigh属性用于计算两点之间的宽和高。usingSystem;classPointintx,y;publicintXgetreturnx;publicintYgetreturny;,【例3.21】,publicPoint()x=y=0;publicPoint(intx,inty)this.x=x;this.y=y;classLabelPointp1=newPoint();Pointp2=newPoint(5,

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论