1usingSystem;classA{protectedinta;publicA(intx){a=x;}publicvoidF(){Console.WriteLine(A.F:“+a);}}classB:A{intb;publicB(intx,inty):base(x){b=y;}newpublicvoidF(){Console.WriteLine(“B.F:“+a+”,”+b);}}classTest{staticvoidMain(){Aa=newA(1);Bb=newB(2,3);a.F();a=b;//可以将派生类对象的引用赋值给基类变量!反之不行!a.F();//仍执行基类A的方法!}}输出结果为:A.F:1补充赋值兼容对F方法静态绑定A.F:22usingSystem;classA{publicvirtualvoidF(){Console.WriteLine(A.F);}}classB:A{publicoverridevoidF(){Console.WriteLine(“B.F”);}}classTest{staticvoidMain(){Aa=newA();Bb=newB();a.F();a=b;//或a=newB();a.F();}}【例】虚方法。输出结果为:A.FB.F多态性示例1对F虚方法动态绑定定义虚方法重载虚方法3补充:虚方法及其重载的应用实例•如图所示,父类“运动员”有3个子类,这3个子类都可以继承父类的“训练”这个方法,但是,某些子类的“训练”可能有不同于父类的内容,此时可以重载父类的训练方法,动态绑定!“跑步!”“游泳!”“传球、射门、跑步!”4用虚方法实现多态的步骤在基类中某方法可以由子类重新定义实现,则实现步骤如下:1.在基类中,可以使用关键字virtual来定义某方法为虚方法(virtualmethod),virtual关键字放在访问级别修饰符和方法返回数据类型之间,格式如下:publicvirtualvoidTrain(){Console.WriteLine(“跑步!”);}2.子类继承父类之后,可以使用override关键字覆盖父类中的虚方法,并重新给出实现,格式如下:publicoverridevoidTrain(){……}另外,子类也可以不重载基类的虚方法,直接使用基类的实现。5根据上面给出的语法,定义基类如下:///summary///基类:运动员////summarypublicclassPlayer{///summary///虚方法:训练////summarypublicvirtualvoidTrain(){Console.WriteLine(“跑步!”);}}代码定义了基类Player,它有一个虚方法Train();用虚方法实现例题6【虚方法实现例6-1】下面的代码分别实现3个子类运动员。///summary///足球运动员////summarypublicclassFootballPlayer:Player{publicoverridevoidTrain(){Console.WriteLine(“传球、射门、跑步!);}}用虚方法实现例题7///summary///游泳运动员////summarypublicclassSwimPlayer:Player{publicoverridevoidTrain(){Console.WriteLine(“游泳!);}}用虚方法实现例题8///summary///短跑运动员////summarypublicclassSprinters:Player{}•说明:以上的足球运动员类和游泳运动员类继承了运动员类,并重新实现了其父类中的虚方法Train()。而子类短跑运动员类则没有重载该虚方法。用虚方法实现例题96.2.1什么是多态继续上一节给出的例子,现在假设你是一个运动员总教练,手下有足球、游泳、短跑运动员。你把运动员召集起来之后,如果你是只是对他们说“去训练吧!”,那么他们会怎样做呢?很显然,不同项目的运动员会去做不同的训练。对于总教练而言,只需要告诉他们的统一的指令Train即可。在面向对象的思想中,这称为多态(Polymorphism)。10上面介绍了多态的含义,现在使用上一节的运动员示例来介绍其具体实现。其中,对基类及其3个子类的实现代码不变,不再重复。下面说明了如何使用多态性来对运动员们统一发号施令。6.2.2多态的实现11classMainTest{staticvoidMain(string[]args){Playerp;p=newFootballPlayer();p.Train();//调用虚方法,注意动态绑定!p=newSwimPlayer();p.Train();p=newSprinters();p.Train();}}}6.2.2多态的实现运行结果:传球、射门、跑步!游泳!跑步!12说明:代码声明了一个运动员对象p,然后对其赋值为足球运动员,并调用Train()方法让其训练;同样,再使其成为一个游泳运动员,同样使用Train()方法让其训练。只看“p.Train();”这行代码,对其所调用的方法是一样的,但由于p的训练项目不同,因此,根据多态的性质,p调用了不同的训练方法。6.2.2多态的实现136.1.1什么是抽象类•上例中基类的“训练”方法实际上可以不给出具体实现,因为无法用统一的训练方法去针对所有不同的子类运动员。可见,在“运动员”类中,“训练”只是一个纸上谈兵的方法,是一个“纯虚拟”的方法。•把“训练”方法从运动员中间去掉呢?事实上,“训练”的存在是有其意义的,这个“虚拟”方法也并不是全无用处,为其子类设置一个必须包含的方法。•在此,我们把“训练”这个方法称为“抽象方法”,把“运动员”这个父类称为“抽象类”。146.1.1什么是抽象类在C#中,抽象方法和抽象类的定义如下:抽象方法:包含方法定义,但没有具体实现的方法,需要其子类或者子类的子类来具体实现。抽象类:含有一个或多个抽象方法的类称为抽象类。抽象类不能够被实例化,这是因为它包含了没有具体实现的方法。抽象方法抽象类分别实现基类的抽象方法156.1.2声明抽象类现在来看抽象方法和抽象类如何在C#中实现。1.在C#中,使用关键字abstract来定义抽象方法(abstractmethod),并需要把abstract关键字放在访问级别修饰符和方法返回数据类型之间,没有方法实现的部分,格式如下:publicabstractvoidTrain();2.子类继承抽象父类之后,可以使用override关键字覆盖父类中的抽象方法,并做具体的实现,格式如下:publicoverridevoidTrain(){……}另外,子类也可以不实现抽象方法,继续留给其后代实现,这时子类仍旧是一个抽象类。166.1.2声明抽象类根据上面给出的语法,定义运动员抽象类如下:///summary///抽象类:运动员////summarypublicabstractclassPlayer{///summary///抽象方法:训练////summarypublicabstractvoidTrain();}代码定义了运动员抽象类,它有一个抽象方法Train();176.1.3实现抽象方法对于子类来说,在继承了抽象父类之后,就可以具体实现其中的抽象方法了。【例6-1】下面的代码分别实现3个子类运动员的抽象方法Train()。///summary///足球运动员////summarypublicclassFootballPlayer:Player{publicoverridevoidTrain(){Console.WriteLine(传球、射门、跑步!);}}186.1.3实现抽象方法///summary///游泳运动员////summarypublicclassSwimPlayer:Player{publicoverridevoidTrain(){Console.WriteLine(游泳!);}}196.1.3实现抽象方法///summary///短跑运动员////summarypublicclassSprinters:Player{publicoverridevoidTrain(){Console.WriteLine(跑步!);}}•说明:以上的足球运动员类继承了运动员类,并实现了其父类中的抽象方法Train()。其余两个子类与此相同,不再赘述。20classMainTest{staticvoidMain(string[]args){Playerp;p=newFootballPlayer();p.Train();//调用虚方法,注意动态绑定!p=newSwimPlayer();p.Train();p=newSprinters();p.Train();}}}6.1.3多态的实现运行结果:传球、射门、跑步!游泳!跑步!216.2.3区分多态和重载如上所述,多态是基于对抽象方法或虚方法的覆盖(override)来实现的,用统一的对外接口来完成不同的功能。在前面,还介绍了重载(overload)的概念,也是使用统一的对外接口来完成不同的功能,那么这两者有什么区别呢?重载,是指允许存在多个同名函数,而这些函数的参数不同。重载的实现是:编译器根据函数不同的参数表,对同名函数的名称加以修饰。对于编译器而言,这些同名函数就成不同的函数。它们的调用地址在编译期间就绑定了。多态,是指子类重新定义父类的虚函数。当子类重新定义了父类的虚函数后,父类根据赋给它的不同的子类,动态调用属于子类的该函数,这样的函数调用在编译期间是无法确定的。不难看出,两者的区别在于编译器何时去寻找所要调用的具体方法,对于重载而言,在函数调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;而对于多态,只有等到函数调用的那一刻,编译器才会确定所要调用的具体函数,这称为称为“晚绑定”或“动态绑定”。226.3接口•前面介绍了虚方法、抽象类和多态的概念。•下面介绍与抽象类非常相似的另一个概念:接口。236.3.1什么是接口接口与抽象类非常相似,它定义了一些未实现的属性和方法。所有继承它的类都继承这些成员,在这个角度上,可以把接口理解为一个类的模板。接口和抽象类的相似之处表现在以下两方面:两者都包含可以由子类继承的抽象成员;两者都不直接实例化。246.3.1什么是接口两者的区别表现在以下几个方面:抽象类除拥有抽象成员之外,还可以拥有非抽象成员;而接口所有的成员都是抽象的。抽象类的抽象成员可以公有或私有,而接口的成员默认都是公有的。接口中不能含有构造函数、析构函数、静态成员和常量。C#只支持单继承,即子类只能继承一个父类,而一个子类却能够继承多个接口。256.3.2声明接口下面通过一个具体的例子,介绍如何在C#中声明和使用接口。图6-2所示是一个IShape接口的示意图。•示例中有一个“形状”的概念,它有3个具体的形状类:矩形、圆形、三角形。可以看出,在某种意义上,接口同完全的抽象类非常相似。266.3.2声明接口C#中声明接口的语法如下:access-modiferinterfaceinterface-name{//interfacemembers}接口的成员访问级别规定为public,因此,不用在声明成员时使用访问级别修饰符。根据上面给出的语法,下面代码来声明这个Ishape接口。///summary///接口:形状////summarypublicinterfaceIshape{doubleGetArea();}276.3.3实现接口声明接口之后,类就可以通过继承接口来实现其中的抽象方法。继承接口的语法同类的继承类似,使用冒号“:”,将待继承的接口放在类的后面。如果继承多个接口,将使用逗号将其分隔。286.3.3实现接口在下面的代码中,实现了矩形类,它继承于