1.15k likes | 1.29k Views
第 10 章 接口、代理和事件. (时间: 3 次课, 6 学时). 第 10 章 接口、代理和事件. 前面已经介绍了有关面向对象程序设计的基本实现技能,本章将介绍一些面向对象程序设计的高级技术:接口、代理和事件。接口和代理都属于 C# 语言的引用数据类型,而事件是 C# 语言新增加的一个成员。由于事件和代理有很密切的关系,所以把事件也放在本章介绍。 本章教学目的: 了解接口和类的区别 掌握接口的定义,实现和使用 掌握创建和使用代理的方法 掌握 Delegate 类和 MulticastDelegate 类实现多重代理的方法 掌握创建和使用事件的方法.
E N D
第10章 接口、代理和事件 (时间:3次课,6学时)
第10章 接口、代理和事件 • 前面已经介绍了有关面向对象程序设计的基本实现技能,本章将介绍一些面向对象程序设计的高级技术:接口、代理和事件。接口和代理都属于C#语言的引用数据类型,而事件是C#语言新增加的一个成员。由于事件和代理有很密切的关系,所以把事件也放在本章介绍。 • 本章教学目的: • 了解接口和类的区别 • 掌握接口的定义,实现和使用 • 掌握创建和使用代理的方法 • 掌握Delegate类和MulticastDelegate类实现多重代理的方法 • 掌握创建和使用事件的方法
第10章 接口、代理和事件 • 10.1 接 口 • 10.2 代 理 • 10.3 事 件
10.1 接 口 • 10.1.1 接口与类 • 10.1.2 接口的定义 • 10.1.3 接口的实现与使用 • 10.1.4 接口映射 • 10.1.5 显式接口成员实现 • 10.1.6 接口实现的继承 • 10.1.7 接口的重新实现 • 10.1.8 接口的查询
10.1 接 口 • 第9章中提到,C#只允许单继承机制。而如果我们希望一个子类继承两个或两个以上更多的父类时,C#语言是不支持的,即C#不允许利用类进行多重继承。但在C#语言中,“多重继承”的功能是通过“接口(Interface)”技术来模拟实现的。
10.1.1 接口与类 • 在C#中需要实现多重继承时,就要使用接口技术。接口具有类似于抽象类的地位,它只具有“被继承”特性,所以接口也像抽象类一样,是一个最高层次的“基类”。但抽象类只能实现单继承,而接口可实现多继承。因此,在C#中,接口和抽象类既有相同的继承特性(最高层“基类”),又有不同的实现机制。 • 抽象类指的是至少包含一个抽象方法的类,而抽象方法指的是被继承时,必须被重写的方法。而接口是另一种类似于抽象类的引用类型,它主要用来声明要定义的类中将包含哪些功能(方法、属性、索引或事件),但不包含这些功能的实例化代码(同抽象类),只在“继承”(通常,在接口技术中称为“实现”,以便与类中的“继承”有所区别)时才实例化这些功能的代码(也同抽象类)。换句话说,接口只是定义了类必须做什么,而不是怎样做。 • 一旦定义了一个接口,许多类都可以实现它。所谓“实现接口”就是意味着某个将要使用这个接口的类,必须为该接口所定义的方法、属性、索引或事件提供实体(实现的代码)。
10.1.1 接口与类 • 所以说接口类似于抽象类,但它与类之间有以下区别: • 接口只提供类所需实现的方法、属性、索引或事件的格式或约定,不提供任何相应的功能代码。具体的功能代码由继承(使用)该接口的类或结构来实现,这叫作“接口实现”。 • 接口中只包含方法、属性、索引或事件,而不包含任何数据成员、构造函数、析构函数和静态函数,而且接口中的所有成员都被视为公有,不能有任何访问修饰符。 • 要实现接口的类必须实现接口中的所有成员,即当一个接口或类从其他接口继承时,它将继承它的基接口中的所有成员。而抽象类则可以根据需要重载部分或全部抽象成员。 • 接口允许多重继承。一个接口可从多个基接口继承,并包含这些基接口继承树上的所有基接口;一个类可以从多个基接口继承;但一个类最多只能有一个直接父类。
10.1.1 接口与类 • 在实际应用中是使用接口还是抽象类为组件提供多态性,一般从以下几点考虑: • 如果预计要创建组件的多个版本,则创建抽象类。抽象类提供简单易行的方法来控制组件版本。通过更新基类,所有继承类都随更改自动更新。另一方面,接口一旦创建就不能更改。如果需要接口的新版本,必须创建一个全新的接口。 • 如果创建的功能将在大范围的全异对象间使用,则使用接口。抽象类应主要用于关系密切的对象,而接口最适合为不相关的类提供通用功能。 • 如果要设计小而简练的功能块,则使用接口。如果要设计大的功能单元,则使用抽象类。 • 如果要在组件的所有实现间提供通用的已实现功能,则使用抽象类。抽象类允许部分实现类,而接口不包含任何成员的实现。
10.1.2 接口的定义 • 1. 接口定义 • 接口定义的语句格式为: • [代码属性] [修饰符] interface 接口名[:基接口列表] • { • …… //接口成员定义体 • }
10.1.2 接口的定义 • 2. 接口成员声明 • 接口成员包括从基接口继承的成员以及接口自身定义的成员。接口成员可以是方法、属性、索引和事件,但不能有常数、运算符、构造函数、析构函数、类型和静态成员。因为接口只具有“被继承”的特性,所以默认时,所有接口成员只具有public特性,接口成员的声明中不能含有任何其他修饰符。
10.1.2 接口的定义 • (1) 接口的方法成员声明格式如下: • [代码属性] [new] 返回值类型 方法名([参数1,参数2,….]); • 接口中只能提供方法的格式声明,而不能包含方法的实现,所以接口方法的声明总是以分号结束。 • 用户可以使用new修饰符在派生的接口中隐藏基接口的同名方法成员,其作用与类中new修饰符的作用相同。例如: • interface IA • { • void Math(); • } • interface IB: IA //接口IB继承接口IA • { • new void Math(); //如果不加new修饰符,将会有警告。加上new就可消除 • }
10.1.2 接口的定义 • 【例10.1】定义一个接口DataSeries,任何类使用该接口可以产生一系列数字。 • public interface DataSeries • { • int getNext(); //返回数字系列中的下一个数字 • void reset(); //重新开始 • void setStart(int x); //设置初始值 • }
10.1.2 接口的定义 • (2) 接口的属性成员声明格式如下: • [代码属性] [new] 属性类型 属性名{get;和/或set;}; • 同理,接口中的属性成员不能包含实现,所以只能以分号结束。在接口属性成员中同样也可以使用new修饰符来隐藏从基接口继承的同名属性成员。接口属性成员的访问方式有只读、只写和可读写3种,如表10.1所示。
10.1.2 接口的定义 表10.1 接口属性的访问方式
10.1.2 接口的定义 • 【例10.2】重写接口DataSeries,通过类MyThree实现接口,同时使用接口属性来获取数列中的下一个元素。 • using System; • public interface DataSeries //接口 • { • int Next //接口的一个属性 • { • get; //返回数列的下一个数字 • set; //设置下一个数字 • } • } • class MyThree: DataSeries //实现接口DataSeries • { • int x; • public MyThree(){x=0; } //构造函数 • public int Next //实现接口属性:获取或设置值 • { • get{ x+=3; return x;} • set{ x=value; } • } • } • class App //应用类 • { • public static void Main() • { • MyThree ob=new MyThree (); • for (int i=0; i<3; i++) //通过属性访问接口 • Console.WriteLine("Next value is "+ ob.Next); • Console.WriteLine("\nStarting at 100"); • ob.Next=100; • for (int i=0; i<3; i++) //通过属性访问接口 • Console.WriteLine("Next value is "+ ob.Next); • } • }
10.1.2 接口的定义 • 程序输出结果如下: • Next value is 3 • Next value is 6 • Next value is 9 • Starting at 100 • Next value is 103 • Next value is 106 • Next value is 109
10.1.2 接口的定义 • (3) 接口的索引器成员声明格式如下: • [代码属性] [new] 数据类型 this [索引器参数类型 参数名] {get; 和/或set;} • 此格式中的[代码属性]和new修饰符的用法和作用,与接口的方法成员和属性成员声明的含义完全一样。 • 声明中的“数据类型”是指索引器引入的元素类型,接口声明中的索引器成员只能用来指定索引器的访问方式,同表10.1。同样不允许在索引器参数上使用out和ref关键字。 • 例如: • interface IA • { • …… • [name("yang yan ") //附加信息(即属性说明) • int this [int index] { ger; set; } //索引器定义 • }
10.1.2 接口的定义 • 【例10.3】以下是接口DataSeries的另一个版本,其中添加了返回第i个元素的只读索引。 • using System; • public interface DataSeries //接口 • { • int Next { get; set; } //接口的一个属性 • int this[int index] //接口的一个索引,一个只读索引 • { • get; //返回数列中的指定数字 • } • } • class MyThree: DataSeries //实现接口DataSeries • { • int x; • public MyThree(){x=0; } //构造函数 • public int Next //接口属性的实现。获取或设置值 • { • get{x+=3; return x;} • set{x=value;} • } • public int this[int index] //实现索引 • { • get • { • x=0; • for(int i=0; i<index; i++) • x+=3; • return x; • } • }
10.1.2 接口的定义 • } • class App //应用类 • { • public static void Main() • { • MyThree ob=new MyThree (); • for (int i=0; i<3; i++) //通过属性访问接口 • Console.WriteLine("Next value is "+ ob.Next); • Console.WriteLine("Starting at 100"); • ob.Next=100; • for (int i=0; i<3; i++) //通过属性访问接口 • Console.WriteLine("Next value is "+ ob.Next); • Console.WriteLine("Resetting to 0"); • ob.Next=0; • for (int i=0; i<3; i++) //通过索引访问接口 • Console.WriteLine("Next value is "+ ob.Next); • } • }
10.1.2 接口的定义 • 程序输出结果如下: • Next value is 3 • Next value is 6 • Next value is 9 • Starting at 100 • Next value is 103 • Next value is 106 • Next value is 109 • Resetting to 0 • Next value is 3 • Next value is 6 • Next value is 9
10.1.2 接口的定义 • (4) 接口的事件成员声明有关“事件”在本章的最后一节介绍,本处只给出接口事件的声明格式: • [代码属性] [new] event 事件代理名 事件名; • 事件声明中的[代码属性]和new修饰符的用法和作用,与接口的方法成员和属性成员的声明含义完全一样。 • 例如: • Interface IA • { • …… //其他成员的定义 • event Click MyEvent; • }
10.1.3 接口的实现与使用 • 以上我们只是定义了一个接口,而在接口中声明的方法、属性、索引或事件的真正实现是由类来完成的。所以说,一旦定义了接口,一个或更多的类就可以以不同的方式来实现该接口中的功能,并且每个类必须实现该接口中所定义的所有方法、属性、索引或事件。即一个接口可以由多个类来实现,而在一个类中也可以实现一个或多个接口。 • 实现接口的方式与继承相同,即将接口放在类名的后面,中间用冒号隔开。实现接口的语句格式: • class 类名:接口名列表 • { • …… //类实体 • } • 其中接口名是指该类所要实现的接口的名称。
10.1.3 接口的实现与使用 • 1. 一个类实现(使用)一个接口。 • 【例10.4】创建一个类MyThree,实现【例10.1】中定义的接口DataSeries,用于生成一系列公差为3的数列。 • using System; • public interface DataSeries //接口DataSeries • { • int getNext(); //返回数字系列中的下一个数字 • void reset(); //重新开始 • void setStart(int x); //设置初始值 • } • class MyThree: DataSeries //实现接口DataSeries的类 • { • int start; • int value; • public MyThree() //构造函数 • { • start=0; • value=0; • } • public int getNext() //实现接口DataSeries中的方法getNext() • { • value+=3; • return value; • }
10.1.3 接口的实现与使用 • public void reset() //实现接口DataSeries中的方法reset() • { • start=0; • value=0; • } • public void setStart(int x) //实现接口DataSeries中的方法setStart() • { • start=x; • value=x; • } • } • class AppDataSeries //应用类 • { • public static void Main() • { • MyThree ob=new MyThree (); • for (int i=0; i<3; i++) //循环输出3个数字 • Console.WriteLine("Next value is "+ob.getNext()); • Console.WriteLine ("Resstting"); //重新开始 • ob.reset(); • for (int i=0; i<3; i++) //循环输出3个数字 • Console.WriteLine("Next value is "+ob.getNext()); • Console.WriteLine ("Starting at 100"); //从100重新开始 • ob.setStart(100); • for (int i=0; i<3; i++) //循环输出3个数字 • Console.WriteLine("Next value is "+ob.getNext()); • } • }
10.1.3 接口的实现与使用 • 程序的运行结果: • Next value is 3 • Next value is 6 • Next value is 9 • Resetting • Next value is 3 • Next value is 6 • Next value is 9 • Starting at 100 • Next value is 103 • Next value is 106 • Next value is 109 • 可见,要实现接口,必须在类名后包括接口,然后提供接口的每一个成员的实现。注意观察上例中接口成员和类中相应的实现的访问类型的写法,在接口成员的声明中不需要任何访问修饰符,而在类中相应接口成员实现定义中则都用public修饰符。
10.1.3 接口的实现与使用 • 类除了可以用来实现接口外,还可以定义它自己的额外成员,如我们在MyThree类中再添加一个方法getPrevious(),用于返回前一个数字的值。 • class MyThree: DataSeries //实现接口DataSeries,并添加getPrevious()方法 • { • int start; • int value; • int prev; • public MyThree() //构造函数 • { • start=0; • value=0; • prev= -3; • } • public int getNext() //实现接口DataSeries中的方法getNext() • { • prev=value; • value+=3;
10.1.3 接口的实现与使用 • return value; • } • public void reset() //实现接口DataSeries中的方法reset() • { • start=0; • value=0; • prev=-3; • } • public void setStart(int x) //实现接口DataSeries中的方法setStart() • { • start=x; • value=x; • prev=x-3; • } • int getPrevious() //DataSeries接口没有定义的方法 • { • return prev; • } • } • 程序中加黑的语句都是由于添加了getPrevious()方法,而改变了接口DataSeries的方法实现。但是这些方法的接口定义没有任何变化,只是实现接口的类中增加了功能代码,这也是接口的优点之一。
10.1.3 接口的实现与使用 • 2. 多个类实现(使用)一个接口 • 【例10.5】我们再用另一个类MyFour来实现接口DataSeries,生成一系列公差为4的数字序列。 • class MyFour: DataSeries //用另一种方式实现接口DataSeries • { • int start; • int value; • public MyFour() //构造函数 • { • start=0; • value=0; • } • public int getNext() //实现接口DataSeries中的方法getNext() • { • value+=4;
10.1.3 接口的实现与使用 • return value; • } • public void reset() //实现接口DataSeries中的方法reset() • { • start=0; • value=0; • } • public void setStart(int x) //实现接口DataSeries中的方法setStart() • { • start=x; • value=x; • } • } • 可以把此处定义的类加到【例10.4】中,则在应用类中的对象可以使用MyFour类的方法来生成公差是4的数列。具体代码构成,用户可自己组织。由此可以看出MyThree和MyFour这些类的对象的接口都是DataSeries接口,也就是说,任何一个类的对象都可以分别实例化接口中定义的功能信息,即接口实现了C#的“一个接口,多种方法”的多态性。
10.1.3 接口的实现与使用 • 3. 一个类实现(使用)多个接口 • 使用接口而不使用继承的最大的好处就是,可以在同一个类中同时实现多个接口,即实现多重继承。 • 要实现多个接口,需将这些接口用逗号分开。
10.1.3 接口的实现与使用 • 【例10.6】在同一个类中实现多个接口。 • using System; • public interface IShape //"图形"接口IShape • { • double Area(); //接口方法,计算图形面积 • double GramLength(); //接口方法,计算图形边长 • int Sides {get;} //接口属性,获取图形的边长 • } • public interface IShapePlay //输出计算结果的接口IShapePlay • { • void Play(); • } • public class Square: IShape, IshapePlay • //实现两个接口的类Square,计算正方形面积 • { • private int sides; //边数 • public int SideLength; //边长 • public Square(){ sides=4; } //构造函数 • public int Sides { get { return sides;} } //实现接口IShape中的属性 • public double Area() • //实现接口IShape中的方法,计算正方形面积 • { • return ((double) (SideLength* SideLength));
10.1.3 接口的实现与使用 • } • public double GramLength () //实现接口IShape中的方法,计算边长 • { • return ((double) (Sides* SideLength)); • } • public void Play() //实现接口IShapePlay中的方法,输出计算结果 • { • Console.WriteLine("\n计算正方形面积结果如下:"); • Console.WriteLine("边长:{0}", this.SideLength); • Console.WriteLine("边数:{0}", this.Sides); • Console.WriteLine("面积:{0}", this.Area()); • } • } • public class MyApp //应用类 • { • public static void Main() • { • Square sq=new Square(); • sq.SideLength=8; • sq.Play(); • } • }
10.1.3 接口的实现与使用 • 程序运行结果: • 计算正方形面积结果如下: • 边长:8 • 边数:4 • 面积:64 • 从Square类代码可见,由于该类包含了两个接口,所以它必须实现这两个接口的所有成员。
10.1.4 接口映射 • 接口映射是指在实现接口的类或结构中定位接口成员的实现的过程。 • 在映射过程中,判断类的某个成员Mc与接口的相应成员Mi匹配的原则是: • 如果Mc和Mi是方法,且它们的名称、返回类型和形式参数都一致,则Mc和Mi匹配。 • 如果Mc和Mi是属性,它们的名称和返回类型都一致,且访问器也相同(如果Mc不是显式实现,则允许Mc具有额外的访问器),则Mc和Mi匹配。 • 如果Mc和Mi是索引器,它们的名称和形式参数都一致,且具有相同的访问器(如果Mc不是显式实现,则允许Mc具有额外的访问器),则Mc和Mi匹配。 • 现假设在类或结构C上执行映射过程,查找接口I的成员M的实现,查找过程如下: • 在类C中查找I.M的实现。如果C中包含一个与I和M相匹配的显式接口成员实现,则这个成员就被认为是I.M的实现。 • 如果在C中没有找到显式接口成员实现,则查找C的所有非静态公共成员,如果某个成员与M相匹配,则被认为是I.M的实现。 • 如果在C中没有找到以上的两种匹配成员,则转到C的基类中继续查找,直到找到匹配的实现。如果一个没有找到,将会提示出错。
10.1.4 接口映射 • 例如: • class MyClass: Interface1 Class1 Interface2 Class2 • { • void MyMethod1() {……. //方法体代码 } • void MyMethod2() {…… //方法体代码 } • } • 以上代码中,MyClass类使用的MyMethod1()方法是在接口Interface1中声明的。当执行上述代码时,先在MyClass类中查找Interface1接口成员的实现,由于在类MyClass中找到了MyMethod1()方法的实现,所以就终止了查找运算。然而,如果在MyClass类没有找到MyMethod1()方法的实现,则还将查找基类Class1和Class2。
10.1.4 接口映射 • 如果类或结构实现了多个包含相同成员的接口,则这些成员可能会映射到同一个实现上。在以下代码段中,接口IA和IB的Play方法成员都被映射到类C的Play()方法的实现上,由于两个接口的同名方法一定是实现不同的显示目标,则当我们调用Play()方法时将会导致歧义性: • interface IA //接口IA • { • void Play (); //方法成员 • } • interface IB //接口IB • { • viod Play(); //方法成员 • } • class C: IA,I B • { • public void Play() {……} • } • 所以在应用问题中,应根据需要使用显式接口成员实现,来分别为各个接口的成员提供实现。
10.1.5 显式接口成员实现 • “显式接口成员实现”是指在类中使用接口成员(方法、属性、索引或事件)的完全限定名来定义实现接口成员。接口成员的完全限定名是指依次用点运算符连接命名空间名、最高层基接口名和各层的派生接口名,最后加上成员名构成整个访问名。例如: • namesapce MySpace • { • interface IMyinterface • { • void MyMethod(); • } • interface IYouinterface • { • void YourMethod(); • } • } • 方法MyMethod()的完全限定名为MySpace.IMyinterface.MyMethod()。
10.1.5 显式接口成员实现 • 创建接口成员的显式实现有两个原因: • 一个类有可能要同时实现两个接口,如果每个接口都定义了同名、同类型的成员,则在类中采用完全限定名来显式实现各接口中的相应成员,就可以消除歧义。 • 使用完全限定名来实现一个接口成员时,在类中该接口成员是私有的。 • 在上述继承接口IA和IB的类C中就必须使用显式接口成员实现来消除歧义性: • class C: IA,I B • { • void IA.Play(){……} //这里不能用public,是私有成员 • void IB.Play(){……} //同上
10.1.5 显式接口成员实现 • 【例10.7】将【例10.6】计算正方形面积的代码修改如下: • using System; • public interface IShape • { • double Area(); //接口方法,计算图形面积 • double GramLength(); //接口方法,计算图形边长 • int Sides {get;} //接口属性,获取图形的边长 • void Play(); //接口方法 • } • public interface IShapePlay • { • void Play(); //接口方法 • } • public class Square: IShape, IShapePlay //实现两个接口的类Square • { • private int sides; //边数 • public int SideLength; //边长 • public Square(){sides=4; }//构造函数 • public int Sides { get { return sides;} }//实现接口IShape中的属性 • public double Area() //实现接口IShape中的方法,计算面积 • { • return ((double) (SideLength* SideLength)); • } • public double GramLength() //计算边长 • { • return ((double) (Sides* SideLength)); • }
10.1.5 显式接口成员实现 • //显示实现IShape接口中的Play()方法,输出文本信息 • void IShape.Play() //注意:这个显式实现是个私有成员,不能用public修饰 • { • Console.WriteLine("\n计算面积结果如下:"); • Console.WriteLine("边长:{0}", this.SideLength); • Console.WriteLine("边数:{0}", this.Sides); • Console.WriteLine("面积:{0}", this.Area()); • } • //显示实现接口IShapePlay中的Play()方法,图形显示 • void IShapePlay.Play() //注意:这个显式实现是个私有成员,不能用public • 修饰 • { • Console.WriteLine("显示图形如下:"); • } • }
10.1.5 显式接口成员实现 • public class MyApp //应用类 • { • public static void Main() • { • Square sq=new Square(); //创建一个对象 sq • sq.SideLength=8; • //定义一个IShape接口变量sh。通过强制转换将对象sq看作一个接口IShape • IShape sh=( IShape)sq; • //定义一个IShapePlay接口变量shp,原理同上 • IShapePlay shp=( IShapePlay)sq; • sh.Play(); //通过接口变量sh调用显式定义方法IShape.Play() • shp.Play(); //通过接口变量shp调用显式定义方法IShapePlay.Play() • } • } • 程序输出结果: • 计算面积结果如下: • 边长:8 • 边数:4 • 面积:64 • 图形显示如下:
10.1.5 显式接口成员实现 • 本例使用了方法的完全限定名(接口名.方法名),在同一个类Square中显式地实现了两个接口IShape和IShapePlay中的同名方法Play(),在应用类中对定义的显式方法进行了调用。对显式实现的方法的调用不能使用“类名.方法”的格式,因为用类名调用方法不能明确调用哪个方法,所以,要调用不同接口的某个同名方法时,一般有两个常用的方法: • 本例中的方法。将类对象强制转换为相应的接口变量,在本例中就是将类对象sq强制转换为接口IShape或IshapePlay的两个相应接口变量sh和shp,可以用这些接口变量来调用相应的接口方法Play()。 • 在实现方法的类中,分别显式实现两个同名方法,由于显示实现的方法是私有的,所以在类中再相应创建两个用于对这两个显式方法进行调用的调用方法,称其为接口引用调用方法。在应用类中则只要像正常情况下用“对象名.接口引用调用方法名”调用方法即可。这种方法的代码如下,接口代码同【例10.8】,以下只给出实现方法的类和应用类代码:
10.1.5 显式接口成员实现 • public class Square: IShape, IShapePlay //实现两个接口的类Square • { • private int sides; //边数 • public int SideLength; //边长 • public Square(){sides=4; }//构造函数 • public int Sides { get { return sides;} } //实现接口IShape中的属性 • public double Area() //实现接口IShape中的方法,计算面积 • { • return ((double) (SideLength* SideLength)); • } • public double GramLength() //计算边长 • { • return ((double) (Sides* SideLength)); • } • //显示实现IShape接口中的Play()方法,输出文本信息 • void IShape.Play() //注意:这个显式实现是个私有成员,不能用public修饰 • { • Console.WriteLine("\n计算面积结果如下:"); • Console.WriteLine("边长:{0}", this.SideLength); • Console.WriteLine("边数:{0}", this.Sides); • Console.WriteLine("面积:{0}", this.Area()); • }
10.1.5 显式接口成员实现 • //显示实现接口IShapePlay中的Play()方法,图形显示 • void IShapePlay.Play() //注意:这个显式实现是个私有成员,不能用public修饰 • { • Console.WriteLine("显示图形如下:"); • } • //通过接口引用调用方法Play() • public void Play_A() • { • IShape ob_a=this; //当前对象的引用 • ob_a.Play(); //调用IShape.Play()方法 • } • public void Play_B() • { • IShapePlay ob_b=this; //当前对象的引用 • ob_b.Play(); //调用IShapePlay.Play()方法 • } • } • public class MyApp //应用类 • { • public static void Main() • { • Square sq=new Square(); //创建一个对象 sq • sq.SideLength=8; • sq.Play_A(); //调用IShape.Play()方法 • sq.Play_B(); //调用IShapePlay.Play()方法 • } • }
10.1.6 接口实现的继承 • 1. 接口之间的继承 • 在C#中,一个接口可以继承另一个接口,语法和类的继承一样。当一个类要实现从另一个接口继承来的接口,该类就必须提供接口继承链中定义的所有成员的实现。
10.1.6 接口实现的继承 • 【例10.8】一个接口继承另一个接口。 • using System; • public interface IA //接口IA • { • void meth1(); • void meth2(); • } • public interface IB:IA //接口IB继承接口IA { • void meth3(); • } • class Myclass: IB //该类必须实现接口IA和IB的所有成员 • { • public void meth1() • { • Console.WriteLine("实现meth1()方法"); • } • public void meth2 () • { • Console.WriteLine("实现meth2()方法");
10.1.6 接口实现的继承 • } • public void meth3() • { • Console.WriteLine("实现meth3()方法"); • } • } • class App • { • public static void Main() • { • Myclass ob=new Myclass (); • ob.meth1(); • ob.meth2(); • ob.meth3(); • } • } • 程序运行结果: • 实现meth1()方法 • 实现meth2()方法 • 实现meth3()方法
10.1.6 接口实现的继承 • 当一个接口继承另一个接口时,如果派生接口的成员和基接口的成员同名,则基接口的成员就会被隐藏,这与类继承时发生的情况一样。如果派生接口的成员不用new修饰符,编译时会发出警告信息。加上new修饰符后即可消除警告。读者可以通过对本例代码的适当修改,来观察隐藏基接口成员的现象。 • 如果某个接口含有隐藏成员,只有使用new修饰符的成员可被调用。在以下代码中,接口ISon中的方法成员X()隐藏了基接口IFather中的属性成员X: • interface IFather • { • int X {get;} • } • interface ISon : IFather • { • new int X(); //此处的方法成员X()将隐藏基接口中的同名属性成员X • }
10.1.6 接口实现的继承 • 对被隐藏的属性成员X必须提供显式接口成员实现来消除冲突,以下给出了在类中的3种不同的实现方法: • 两个接口中的成员都使用显式接口成员实现方式来实现 • class C : ISon • { • int IFather.X {get {……}} //使用显式接口成员方式 • int ISon.X() {……} //使用显式接口成员方式 • } • 接口ISon中的X成员使用显式接口成员实现方式来实现 • class C: ISon • { • public int X {get {……}} • int ISon.X( ) {……} //使用显式接口成员方式 • } • 接口IFather中的X成员使用显式接口成员实现方式来实现 • class C : ISon • { • int IFather.X {get {……}} //使用显式接口成员方式 • int X() {……} • }
10.1.6 接口实现的继承 • 在多重继承的情况下,如果基接口的一个成员被它的一个派生接口中的同名成员隐藏,则该成员也将在这个派生接口的所有派生接口中都处于隐藏状态。例如: • interface IFather //基接口IFather • { • void F(int x); • } • interface IBoy: IFather //派生接口IBoy • { • new void F(int x); //隐藏基接口IFather中的成员F() • } • interface IGirl: IFather //派生接口IGirl • { • void G(); • } • interface IMuilt : IBoy, IGirl { } //也隐藏IBoy的基接口IFather中的成员F() • class App • { • void method(IMuilt y) • { • y.F(1); //调用IBoy.F,因为IFather成员被派生接口IBoy中的F()成员所隐藏 • ((IFather)y).F(1); //调用IFather.F • ((IBoy)y).F(1); //调用IBoy.F • ((IGirl)y).F(1); //调用IFather.F,IFather成员F()没有被派生接口IGirl中的成 • 员所隐藏 • } • }