第7章 继承和接口设计精选PPT.ppt
第7章 继承和接口设计第1页,本讲稿共60页7.1.1什么是继承什么是继承 一个类从另一个类派生出来时,称之为派生类或子类,被派生的一个类从另一个类派生出来时,称之为派生类或子类,被派生的类称为基类或父类。类称为基类或父类。派生类从基类那里继承特性,派生类也可以作为其他类的基类,派生类从基类那里继承特性,派生类也可以作为其他类的基类,从一个基类派生出来的多层类形成了类的层次结构。从一个基类派生出来的多层类形成了类的层次结构。7.1 继继 承承第2页,本讲稿共60页 C#中只允许中只允许单继承单继承,即一个派生类只能有一个基类。,即一个派生类只能有一个基类。C#中继承是中继承是可传递可传递的,如果的,如果C从从B派生,派生,B从从A派生,那么派生,那么C不仅继承不仅继承B的成员,的成员,还继承还继承A的成员。的成员。C#中派生类中派生类可添加新成员可添加新成员,但不能删除基类的成员。,但不能删除基类的成员。C#中派生类中派生类不能继承基类的构造函数和析构函数不能继承基类的构造函数和析构函数,但能继承基类的属性。,但能继承基类的属性。C#中派生类中派生类可隐藏基类的同名成员可隐藏基类的同名成员,如果在派生类可以隐藏了基类的同名成员,基,如果在派生类可以隐藏了基类的同名成员,基类该成员在派生类中就不能被直接访问,只能通过类该成员在派生类中就不能被直接访问,只能通过“base.基类方法名基类方法名”来访问。来访问。C#中派生类对象也是基类的对象,但基类对象却不一定是基派生类的对象。也就是中派生类对象也是基类的对象,但基类对象却不一定是基派生类的对象。也就是说,基类的引用变量可以引用基派生类对象,而派生类的引用变量不可以引用基类对说,基类的引用变量可以引用基派生类对象,而派生类的引用变量不可以引用基类对象。象。C#C#中的继承具有以下特点:中的继承具有以下特点:第3页,本讲稿共60页7.1.2 派生类的声明派生类的声明 派生类的声明格式如下:派生类的声明格式如下:类修饰符类修饰符 class 派生类:基类派生类:基类;C#中派生类可以从它的基类中继承字段、属性、方法、事件、中派生类可以从它的基类中继承字段、属性、方法、事件、索引器等。索引器等。实际上除了构造函数和析构函数,派生类隐式地继承了基实际上除了构造函数和析构函数,派生类隐式地继承了基类的所有成员。类的所有成员。第4页,本讲稿共60页class A private int n;/私有字段私有字段 protected int m;/保护的字段保护的字段 public void afun()/公有方法公有方法 /方法的代码方法的代码 class B:A private int x;/私有字段私有字段 public void bfun()/公有方法公有方法 /方法的代码方法的代码 从中看出从中看出Base_fun()方法在方法在B类类中不用重写,因为中不用重写,因为B类继承了类继承了A类,所以可以不用重写类,所以可以不用重写A类中类中的的Base_fun()方法,就可以被方法,就可以被B类调用。类调用。在主函数中包含以下代码:在主函数中包含以下代码:B b=new B();/定义对象并实例化定义对象并实例化 b.afun();第5页,本讲稿共60页7.1.3基类成员的可访问性基类成员的可访问性 派生类将获取基类的所有非私有数据和行为。派生类将获取基类的所有非私有数据和行为。如果希望在派生类中隐藏某些基类的成员,可以在基类中将这些成员如果希望在派生类中隐藏某些基类的成员,可以在基类中将这些成员设为设为private访问成员。访问成员。第6页,本讲稿共60页7.1.4 按次序调用构造函数和析构函数按次序调用构造函数和析构函数1.调用默认构造函数的次序调用默认构造函数的次序 如果类是从一个基类派生出来的,那么在调用这个派生类的如果类是从一个基类派生出来的,那么在调用这个派生类的默认构造函数之前会调用基类的默认构造函数。调用的次序将从默认构造函数之前会调用基类的默认构造函数。调用的次序将从最远的基类开始。最远的基类开始。第7页,本讲稿共60页class A/基类基类 public A()Console.WriteLine(调用类调用类A的构造函数的构造函数);class B:A/从从A派生类派生类B public B()Console.WriteLine(调用类调用类B的构造函数的构造函数);class C:B/从从B派生类派生类C public C()Console.WriteLine(调用类调用类C的构造函数的构造函数);在主函数中执行以下语句:在主函数中执行以下语句:C b=new C();/定义对象并实例化定义对象并实例化运行结果如下:运行结果如下:调用类调用类A的构造函数的构造函数调用类调用类B的构造函数的构造函数调用类调用类C的构造函数的构造函数第8页,本讲稿共60页2.调用默认析构函数的次序调用默认析构函数的次序 当销毁对象时,它会按照相反的顺序来调用析构函数。首先调用当销毁对象时,它会按照相反的顺序来调用析构函数。首先调用派生类的析构函数,然后是最近基类的析构函数,最后才调用那个派生类的析构函数,然后是最近基类的析构函数,最后才调用那个最远的析构函数。最远的析构函数。第9页,本讲稿共60页class A/基类基类 A()Console.WriteLine(调用类调用类A的析构函数的析构函数);class B:A/从从A派生类派生类B B()Console.WriteLine(调用类调用类B的析构函数的析构函数);class C:B/从从B派生类派生类C C()Console.WriteLine(调用类调用类C的析构函数的析构函数);在主函数中执行语句在主函数中执行语句C b=new C();其运行结果如下:其运行结果如下:调用类调用类C的析构函数的析构函数调用类调用类B的析构函数的析构函数调用类调用类A的析构函数的析构函数第10页,本讲稿共60页3.调用重载构造函数的次序调用重载构造函数的次序 调用基类的重载构造函数需使用调用基类的重载构造函数需使用base关键字。关键字。base关键字主要是为派关键字主要是为派生类调用基类成员提供一个简写的方法,可以在子类中使用生类调用基类成员提供一个简写的方法,可以在子类中使用base关键字关键字访问的基类成员。调用基类中重载构造函数的方法是将派生类的重载构访问的基类成员。调用基类中重载构造函数的方法是将派生类的重载构造函数作如下设计:造函数作如下设计:public 派生类名派生类名(参数列表参数列表1):base(参数列表参数列表2)其中,其中,“参数列表参数列表2”和和“参数列表参数列表1”存在对应关系。存在对应关系。同样,在通过同样,在通过“参数列表参数列表1”创建派生类的实例对象时,先创建派生类的实例对象时,先以以“参数列表参数列表2”调用基类的构造函数,再调用派生类的构造调用基类的构造函数,再调用派生类的构造函数。函数。第11页,本讲稿共60页【例【例7.1】分析以下程序的运行结果。分析以下程序的运行结果。using System;namespace Proj7_1class A private int x;publicA()Console.WriteLine(调用类调用类A的构造函数的构造函数);public A(int x1)x=x1;Console.WriteLine(调用类调用类A的重载构造函数的重载构造函数);A()Console.WriteLine(A:x=0,x);class B:A private int y;publicB()Console.WriteLine(调用类调用类B的构造函数的构造函数);public B(int x1,int y1):base(x1)y=y1;Console.WriteLine(调用类调用类B的重载构造函数的重载构造函数);B()Console.WriteLine(B:y=0,y);第12页,本讲稿共60页 class C:B private int z;publicC()Console.WriteLine(调用类调用类C的构造函数的构造函数);public C(int x1,int y1,int z1):base(x1,y1)z=z1;Console.WriteLine(调用类调用类C的重载构造函数的重载构造函数);C()Console.WriteLine(C:z=0,z);class Program static void Main(string args)C c=new C(1,2,3);第13页,本讲稿共60页7.1.5 使用使用sealed修饰符来禁止继承修饰符来禁止继承 C#中提供了中提供了sealed关键字用来禁止继承。要禁止继承一个类,只关键字用来禁止继承。要禁止继承一个类,只需要在声明类时加上需要在声明类时加上sealed关键字就可以了,这样的类称为密封类。关键字就可以了,这样的类称为密封类。例如:例如:sealed class 类名类名 这样就不能从该类派生任何子类。这样就不能从该类派生任何子类。第14页,本讲稿共60页7.2.1 什么是多态性什么是多态性 面向对象程序设计中的多态性是一个重要的概念。所谓多态面向对象程序设计中的多态性是一个重要的概念。所谓多态性,就是性,就是同一签名具有不同的表现行为同一签名具有不同的表现行为,运算符重载和函数,运算符重载和函数重载都属于多态性的表现形式。重载都属于多态性的表现形式。7.2 多态性多态性第15页,本讲稿共60页7.2.2 隐藏基类方法隐藏基类方法当派生类从基类继承时,它会获得基类的所有方法、字当派生类从基类继承时,它会获得基类的所有方法、字段、属性和事件。若要更改基类的数据和行为,有两种选择:段、属性和事件。若要更改基类的数据和行为,有两种选择:方法方法1:使用新的派生成员替换基成员:使用新的派生成员替换基成员方法方法2:重写虚拟的基成员。:重写虚拟的基成员。第16页,本讲稿共60页方法方法1示例:在使用新的派生方法替换基方法时应使用示例:在使用新的派生方法替换基方法时应使用new关键关键字。例如:字。例如:class A public void fun()Console.WriteLine(A);class B:A new public void fun()/隐藏基类方法隐藏基类方法fun Console.WriteLine(B);在主函数中执行以下语句:在主函数中执行以下语句:B b=new B();b.fun();运行结果如下:运行结果如下:B第17页,本讲稿共60页7.2.3 重写重写重写是指在子类中编写有相同名称和参数的方法。重写是指在子类中编写有相同名称和参数的方法。重写和重载的区别:重写和重载的区别:后者是指编写(在同一个类中)具有相同后者是指编写(在同一个类中)具有相同的名称,却有不同的参数的方法。也就是说,重写是指子类中的方的名称,却有不同的参数的方法。也就是说,重写是指子类中的方法与基类中的方法具有相同的签名,而重载方法具有不同的签名。法与基类中的方法具有相同的签名,而重载方法具有不同的签名。第18页,本讲稿共60页1.virtual关键字关键字 virtual关键字用于修饰方法、属性、索引器或事件声明,并且关键字用于修饰方法、属性、索引器或事件声明,并且允许在派生类中重写这些对象。例如,以下定义了一个虚拟方法允许在派生类中重写这些对象。例如,以下定义了一个虚拟方法并可被任何继承它的类重写:并可被任何继承它的类重写:public virtual double Area()return x*y;调用虚方法时,首先调用派生类中的该重写成员,如果没调用虚方法时,首先调用派生类中的该重写成员,如果没有派生类重写该成员,则它可能是原始成员。有派生类重写该成员,则它可能是原始成员。注意:默认情况下,方法是非虚拟的,不能重写非虚方法。注意:默认情况下,方法是非虚拟的,不能重写非虚方法。virtual修饰符不能与修饰符不能与static、abstract和和override修饰符一起使用。修饰符一起使用。在静态属性上使用在静态属性上使用virtual修饰符是错误的。修饰符是错误的。第19页,本讲稿共60页2.重写方法重写方法 override方法提供从基类继承的成员的新实现。通过方法提供从基类继承的成员的新实现。通过override声明声明重写的方法称为重写基方法。重写的基方法必须与重写的方法称为重写基方法。重写的基方法必须与override方法具有方法具有相同的签名。相同的签名。注意:不能重写非虚方法或静态方法。重写的基方法必须是注意:不能重写非虚方法或静态方法。重写的基方法必须是virtual、abstract或或override的。的。第20页,本讲稿共60页【例【例7.2】分析以下程序的运行结果。分析以下程序的运行结果。using System;namespace Proj7_2class Student protected int no;/学号学号 protected string name;/姓名姓名 protected string tname;/班主任或指导教师班主任或指导教师 public void setdata(int no1,string name1,string tname1)no=no1;name=name1;tname=tname1;public virtual void dispdata()/虚方法虚方法 Console.WriteLine(本科生学号本科生学号:0 姓名姓名:1 班班 主主 任任:2,no,name,tname);第21页,本讲稿共60页 class Graduate:Student public override void dispdata()/重写方法重写方法 Console.WriteLine(研究生研究生 学号学号:0 姓名姓名:1 指导教师指导教师:2,no,name,tname);class Program static void Main(string args)Student s=new Student();s.setdata(101,王华王华,李量李量);s.dispdata();Graduate g=new Graduate();g.setdata(201,张华张华,陈军陈军);g.dispdata();第22页,本讲稿共60页【例【例7.3】设计一个控制台应用程序,采用虚方法求长方形、圆、圆球体设计一个控制台应用程序,采用虚方法求长方形、圆、圆球体和圆柱体的面积或表面积。和圆柱体的面积或表面积。using System;namespace Proj7_3 public class Rectangle /长方形类长方形类 public const double PI=Math.PI;protected double x,y;public Rectangle()public Rectangle(double x1,double y1)x=x1;y=y1;public virtual double Area()/求面积求面积 return x*y;第23页,本讲稿共60页 public class Circle:Rectangle /圆类圆类 public Circle(double r):base(r,0)public override double Area()/求面积求面积 return PI*x*x;class Sphere:Rectangle /圆球体类圆球体类 public Sphere(double r):base(r,0)public override double Area()/求面积求面积 return 4*PI*x*x;class Cylinder:Rectangle /圆柱体类圆柱体类 public Cylinder(double r,double h):base(r,h)public override double Area()/求面积求面积 return 2*PI*x*x+2*PI*x*y;第24页,本讲稿共60页 class Program static void Main(string args)double x=2.4,y=5.6;double r=3.0,h=5.0;Rectangle t=new Rectangle(x,y);Rectangle c=new Circle(r);Rectangle s=new Sphere(r);Rectangle l=new Cylinder(r,h);Console.WriteLine(长为长为0,宽为宽为1的长方形面积的长方形面积=2:F2,x,y,t.Area();Console.WriteLine(半径为半径为0的圆面积的圆面积=1:F2,r,c.Area();Console.WriteLine(半径为半径为0的圆球体表面积的圆球体表面积=1:F2,r,s.Area();Console.WriteLine(半径为半径为0,高度为高度为1的圆柱体表面积的圆柱体表面积=2:F2,r,h,l.Area();第25页,本讲稿共60页7.3 抽象类抽象类7.3.1 什么是抽象类什么是抽象类 在类声明中使用在类声明中使用abstract修饰符的类称为抽象类。抽象类具有修饰符的类称为抽象类。抽象类具有以下特点:以下特点:抽象类不能实例化。抽象类不能实例化。抽象类可以包含抽象方法和抽象访问器。抽象类可以包含抽象方法和抽象访问器。抽象类中可以存在非抽象的方法。抽象类中可以存在非抽象的方法。不能用不能用sealed修饰符修改抽象类,这也意味着抽象类不能被继承。修饰符修改抽象类,这也意味着抽象类不能被继承。从抽象类派生的非抽象类必须包括继承的所有抽象方法和抽从抽象类派生的非抽象类必须包括继承的所有抽象方法和抽象访问器的实现。象访问器的实现。抽象类可以被抽象类所继承,结果仍是抽象类。抽象类可以被抽象类所继承,结果仍是抽象类。第26页,本讲稿共60页7.3.2 抽象方法抽象方法 在方法声明中使用在方法声明中使用abstract修饰符以指示方法不包含实现的,修饰符以指示方法不包含实现的,即为抽象方法。抽象方法具有以下特性:即为抽象方法。抽象方法具有以下特性:声明一个抽象方法使用声明一个抽象方法使用abstract关键字。关键字。抽象方法是隐式的虚方法。抽象方法是隐式的虚方法。只允许在抽象类中使用抽象方法声明。只允许在抽象类中使用抽象方法声明。一个类中可以包含一个或多个抽象方法。一个类中可以包含一个或多个抽象方法。因为抽象方法声明不提供实际的实现,所以没有方法体;方法声明因为抽象方法声明不提供实际的实现,所以没有方法体;方法声明只是以一个分号结束,并且在签名后没有大括号只是以一个分号结束,并且在签名后没有大括号。抽象方法实现由一个重写方法提供,此重写方法是非抽象类的成员。抽象方法实现由一个重写方法提供,此重写方法是非抽象类的成员。实现抽象类用实现抽象类用“:”,实现抽象方法用,实现抽象方法用override关键字。关键字。在抽象方法声明中使用在抽象方法声明中使用static或或virtual修饰符是错误的。修饰符是错误的。抽象方法被实现后,不能更改修饰符。抽象方法被实现后,不能更改修饰符。第27页,本讲稿共60页【例【例7.4】分析以下程序的运行结果。分析以下程序的运行结果。using System;namespace Proj7_4abstract class A/抽象类声明抽象类声明 abstract public int fun();/抽象方法声明抽象方法声明 class B:A int x,y;public B(int x1,int y1)/抽象方法实现抽象方法实现 x=x1;y=y1;public override int fun()return x*y;第28页,本讲稿共60页 class Program static void Main(string args)B b=new B(2,3);Console.WriteLine(0,b.fun();6第29页,本讲稿共60页7.3.3 抽象属性抽象属性 除了在声明和调用语法上不同外,抽象属性的行为与抽象除了在声明和调用语法上不同外,抽象属性的行为与抽象方法类似,另外,抽象属性具有如下特性:方法类似,另外,抽象属性具有如下特性:在静态属性上使用在静态属性上使用abstract修饰符是错误的。修饰符是错误的。在派生类中,通过包括使用在派生类中,通过包括使用override修饰符的属性声明,可以重写抽象的继修饰符的属性声明,可以重写抽象的继承属性。承属性。抽象属性声明不提供属性访问器的实现,它只声明该类支持属性,而将抽象属性声明不提供属性访问器的实现,它只声明该类支持属性,而将访问器实现留给其派生类。访问器实现留给其派生类。第30页,本讲稿共60页【例【例7.5】分析以下程序的运行结果。分析以下程序的运行结果。using System;namespace Proj7_5abstract class A/抽象类声明抽象类声明 protected int x=2;protected int y=3;public abstract void fun();/抽象方法声明抽象方法声明 public abstract int px get;set;/抽象属性声明抽象属性声明 public abstract int py get;/抽象属性声明抽象属性声明 第31页,本讲稿共60页 class B:A public override void fun()/抽象方法实现抽象方法实现 x+;y+;public override int px/抽象属性实现抽象属性实现 set x=value;get return x+10;public override int py/抽象属性实现抽象属性实现 get return y+10;class Program static void Main(string args)B b=new B();b.px=5;b.fun();Console.WriteLine(x=0,y=1,b.px,b.py);x=16,y=14第32页,本讲稿共60页7.4 接口接口7.4.1 什么是接口什么是接口 接口是类之间交互内容的一个抽象,把类之间需要交互的内容抽接口是类之间交互内容的一个抽象,把类之间需要交互的内容抽象出来定义成接口,可以更好的控制类之间的逻辑交互。接口具有象出来定义成接口,可以更好的控制类之间的逻辑交互。接口具有下列特性:下列特性:接口类似于抽象基类。继承接口的任何非抽象类型都必须实现接口的所有成员。接口类似于抽象基类。继承接口的任何非抽象类型都必须实现接口的所有成员。不能直接实例化接口。不能直接实例化接口。接口可以包含事件、索引器、方法和属性。接口可以包含事件、索引器、方法和属性。接口不包含方法的实现。接口不包含方法的实现。类和结构可从多个接口继承。类和结构可从多个接口继承。接口自身可从多个接口继承。接口自身可从多个接口继承。接口只包含成员定义,不包含成员的实现,成员的实接口只包含成员定义,不包含成员的实现,成员的实现需要在继承的类或者结构中实现。接口的成员包括方现需要在继承的类或者结构中实现。接口的成员包括方法、属性、索引器和事件,但接口不包含字段。法、属性、索引器和事件,但接口不包含字段。第33页,本讲稿共60页7.4.2 接口的定义接口的定义1.声明接口声明接口 一个接口声明属于一个类型说明,其一般语法格式如下:一个接口声明属于一个类型说明,其一般语法格式如下:接口修饰符接口修饰符 interface 接口名接口名:父接口列表父接口列表 /接口成员定义体接口成员定义体 其中,接口修饰符可以是其中,接口修饰符可以是new、public、protected、internal和和private。new修饰符是在嵌套接口中唯一被允许存在的修饰符,表示用相同的修饰符是在嵌套接口中唯一被允许存在的修饰符,表示用相同的名称隐藏一个继承的成员。名称隐藏一个继承的成员。第34页,本讲稿共60页2.接口的继承接口的继承 接口可以从零个或多个接口中继承。当一个接口从多个接口中继接口可以从零个或多个接口中继承。当一个接口从多个接口中继承时,用承时,用“:”后跟被继承的接口名称,这多个接口之间用后跟被继承的接口名称,这多个接口之间用“,”号号分隔。被继承的接口应该是可以被访问的,即不能从分隔。被继承的接口应该是可以被访问的,即不能从internal或或internal类型的接口继承。类型的接口继承。对一个接口的继承也就继承了接口的所有成员。对一个接口的继承也就继承了接口的所有成员。例如:例如:public interface Ia/接口接口Ia声明声明 void mymethod1();public interface Ib/接口接口Ib声明声明 int mymethod2(int x);public interface Ic:Ia,Ib/接口接口Ic从从Ia和和Ib继承继承 第35页,本讲稿共60页7.4.3 接口的成员接口的成员 接口可以声明零个或多个成员。一个接口的成员不止包接口可以声明零个或多个成员。一个接口的成员不止包括自身声明的成员,还包括从父接口继承的成员。所有接口括自身声明的成员,还包括从父接口继承的成员。所有接口成员默认都是公有的,接口成员声明中包含任何修饰符都是成员默认都是公有的,接口成员声明中包含任何修饰符都是错误的。错误的。1.接口方法成员接口方法成员 语法格式:语法格式:返回类型返回类型 方法名方法名(参数表参数表);2.接口属性成员接口属性成员 语法格式:语法格式:返回类型返回类型 属性名属性名get;或或 set;例如,以下声明一个接口例如,以下声明一个接口Ia,其中接口属性,其中接口属性x为只读的,为只读的,y为可读为可读可写的,可写的,z为只写的:为只写的:public interface Ia int x get;int y set;get;int z set;第36页,本讲稿共60页3.接口索引器成员接口索引器成员语法格式:语法格式:数据类型数据类型 this索引参数表索引参数表get;或或set;例如:例如:public interface Ia string thisint index get;set;4.接口事件成员接口事件成员语法格式:语法格式:event 代表名代表名 事件名事件名;例如:例如:public delegate void mydelegate();/声明委托类型声明委托类型 public interface Ia event mydelegate myevent;第37页,本讲稿共60页7.4.4 接口的实现接口的实现 接口的实现分为隐式实现和显式实现。如果类或者结构要实现的是接口的实现分为隐式实现和显式实现。如果类或者结构要实现的是单个接口,可以使用隐式实现,如果类或者结构继承了多个接口,那么单个接口,可以使用隐式实现,如果类或者结构继承了多个接口,那么接口中相同名称成员就要显式实现。显式实现是通过使用接口的完全限接口中相同名称成员就要显式实现。显式实现是通过使用接口的完全限定名来实现接口成员的。定名来实现接口成员的。接口实现的语法格式如下:接口实现的语法格式如下:class 类名:接口名列表类名:接口名列表 /类实体类实体 说明:说明:当一个类实现一个接口时,这个类就必须实现整个接口,而不能选择实现接口当一个类实现一个接口时,这个类就必须实现整个接口,而不能选择实现接口的某一部分。的某一部分。一个接口可以由多个类来实现,而在一个类中也可以实现一个或多个一个接口可以由多个类来实现,而在一个类中也可以实现一个或多个接口。接口。一个类可以继承一个基类,并同时实现一个或多个接口。一个类可以继承一个基类,并同时实现一个或多个接口。第38页,本讲稿共60页1.隐式实现接口成员隐式实现接口成员 如果类实现了某个接口,它必然隐式地继承了该接口成员,如果类实现了某个接口,它必然隐式地继承了该接口成员,只不过增加了该接口成员的具体实现。只不过增加了该接口成员的具体实现。若要隐式实现接口成员,类中的对应成员必须是公共的、非静若要隐式实现接口成员,类中的对应成员必须是公共的、非静态的,并且与接口成员具有相同的名称和签名。态的,并且与接口成员具有相同的名称和签名。第39页,本讲稿共60页【例【例7.6】分析以下程序的运行结果分析以下程序的运行结果。using System;namespace Proj7_6 interface Ia/声明接口声明接口Ia float getarea();/接口成员声明接口成员声明 public class Rectangle:Ia/类类A继承接口继承接口Ia float x,y;public Rectangle(float x1,float y1)/构造函数构造函数 x=x1;y=y1;public float getarea()/隐式接口成员实现隐式接口成员实现,必须使用必须使用public return x*y;第40页,本讲稿共60页 class Program static void Main(string args)Rectangle box1=new Rectangle(2.5f,3.0f);/定义一个类实例定义一个类实例 Console.WriteLine(长方形面积长方形面积:0,box1.getarea();37第41页,本讲稿共60页2.显式实现接口成员显式实现接口成员 当类实现接口时,如给出了接口成员的完整名称即带有当类实现接口时,如给出了接口成员的完整名称即带有接口名前缀,则称这样实现的成员为显式接口成员,其实现接口名前缀,则称这样实现的成员为显式接口成员,其实现被称为显式接口实现。被称为显式接口实现。显式接口成员实现不能使用任何修饰符。显式接口成员实现不能使用任何修饰符。第42页,本讲稿共60页【例【例7.8】分析以下程序的运行结果分析以下程序的运行结果。using System;namespace Proj7_8interface Ia/声明接口声明接口Ia float getarea();/接口成员声明接口成员声明 public class Rectangle:Ia/类类Rectangle继承接口继承接口Ia float x,y;public Rectangle(float x1,float y1)/构造函数构造函数 x=x1;y=y1;float Ia.getarea()/显式接口成员实现显式接口成员实现,带有接口名前缀带有接口名前缀,不能使用不能使用public return x*y;第43页,本讲稿共60页 class Program static void Main(string args)Rectangle box1=new Rectangle(2.5f,3.0f);/定义一个类实例定义一个类实例 Ia ia=(Ia)box1;/定义一个接口实例定义一个接口实例 Console.WriteLine(长方形面积长方形面积:0,ia.getarea();长方形面积长方形面积:7.5第44页,本讲稿共60页7.5 接口在集合排序中的应用接口在集合排序中的应用7.5.1 ArrayList类的排序方法类的排序方法 ArrayList类对象不仅可以存放数值、字符串,还可以存放其他类类对象不仅可以存放数值、字符串,还可以存放其他类的对象和结构变量。其提供的排序方法如下:的对象和结构变量。其提供的排序方法如下:其中涉及到其中涉及到IComparable和和IComparer两个系统接口。两个系统接口。ArrayList.Sort():使用每个元素的:使用每个元素的IComparable接口实现对整个接口实现对整个ArrayList中的元素进行排序。中的元素进行排序。ArrayList.Sort(IComparer):使用指定的比较器对整个:使用指定的比较器对整个ArrayList中的元素进行排序。中的元素进行排序。ArrayList.Sort(Int32,Int32,IComparer):使用指定的比较器对:使用指定的比较器对ArrayList中某个范围内的元素进行排序。中某个范围内的元素进行排序。第45页,本讲稿共60页7.5.2 IComparable接口接口 IComparable接口定义通用的比较方法,由值类型或类实现以接口定义通用的比较方法,由值类型或类实现以创建类型特定的比较方法。其公共成员有创建类型特定的比较方法。其公共成员有CompareTo,它用于,它用于比较当前实例与同一类型的另一对象。其使用语法格式如下:比较当前实例与同一类型的另一对象。其使用语法格式如下:int CompareTo(Object obj)其中,其中,obj表示与此实例进行比较的对象。其返回值是一个表示与此实例进行比较的对象。其返回值是一个32位有符号位有符号整数,指示要比较的对象的相对顺序。返回值的含义如下:整数,指示要比较的对象的相对顺序。返回值的含义如下:小于零:此实例小于小于零:此实例小于obj。零:此实例等于零:此实例等于obj。大于零:此实例大于大于零:此实例大于obj。IComparable接口的接口的CompareTo方法提供默认排序次序,如果方法提供默认排序次序,如果需要改变其排序方式,可以在相关类中实现需要改变其排序方式,可以在相关类中实现CompareTo方法,以方法,以订制其比较功能。订制其比较功能。第46页,本讲稿共60页【例【例7.12】分析以下程序的运行结果。分析以下程序的运行结果。using System;using System.Collections;/新增新增namespace Proj7_12 class Program class Stud:IComparable/从接口派生从接口派生 int xh;/学号学号 string xm;/姓名姓名 int fs;/分数分数 public int pxh /pxh属性属性 get return xh;public string pxm /pxm属性属性 get return xm;public int pfs /pfs属性属性 get return fs;第47页,本讲稿共60页 public Stud(int no,string name,int degree)xh=no;xm=name;fs=degree;public void disp()/输出学生信息输出学生信息 Console.WriteLine(t0t1t2,xh,xm,fs);public int CompareTo(object obj)/实现接口方法实现接口方法 Stud s=(Stud)obj;/转换为转换为Stud实例实例 if(pfs s.pfs)return 1;else if(pfs=s.pfs)return 0;else return-1;static void disparr(ArrayList myarr,string str)/输出所有学生信息输出所有学生信息 Console.WriteLine(str);Console.WriteLine(t学号学号t姓名姓名t分数分数);foreach(Stud s in myarr)s.disp();第48页,本讲稿共60页 static void Main(string args)int i,n=4;ArrayList myarr=new ArrayList();Stud st=new Stud4 new Stud(1,Smith,82),new Stud(4,John,88),new Stud(3,Mary,95),new Stud(2,Cherr,64);for(i=0;i b.pxh)return 1;else if(a.pxh=