怎么事接口

怎么事接口

只要你设计一个和人交换的程序。
先创造一个接口
interface 人 //定义接口,它意味着一个人,
{void Hello(); }//接口虚函数,用来跟这厮谈话

但不同的人有永不的交流形式,具体办法用类来兑现,比如。
class United States人:人 //继承接口“人”
然后,类里实例化接口函数
void Hello(){说hi;}

class 中国人:人 //继承接口“人”
下一场,类里实例化接口函数
void Hello(){说你好;}

class SB:人 //sb也是人
实现 Hello{说xxxxx;}

末尾你的程序运行时,就用接口“人”就可以了,
因为不管境遇怎么样人(米国人,中国人,仍旧sb),都得以和她俩互换了,那就是接口的意义!!! 
  
基于Visual C#的接口基础教程

    
组件化程序设计艺术继承并提高了面向对象的主次设计方式。它把对象技术利用于系统规划,对面向对象的次第设计的落实过程作了更加的虚幻。
  接口是是组件化编程的一个根本内容,熟谙领悟和运用接口将大大减轻编程的工作量,提高代码的可重用性,同时也能使你更透彻的了解面向对象的探究。

首先节 接口慨述

  接口(interface)用来定义一种程序的订立。实现接口的类如故社团要与接口的概念严苛平等。有了这么些协定,就能够遗弃编程语言的限制(理论上)。接口可以从四个基接口继承,而类或社团得以兑现两个接口。接口可以分包方法、属性、事件和索引器。接口本身不提供它所定义的成员的贯彻。接口只指定实现该接口的类或接口必须提供的分子。
     
接口好比一种模版,那种模版定义了对象必须贯彻的点子,其目标就是让这么些方法可以当做接口实例被引用。接口不可能被实例化。类可以兑现两个接口并且通过这多少个实现的接口被索引。接口变量只可以索引实现该接口的类的实例。例子:

 

interface IMyExample {
 string this[int index] { get ; set ; }
 event EventHandler Even ;
 void Find(int value) ;
 string Point { get ; set ; }
}
public delegate void EventHandler(object sender, Event e) ; 

 

  下面例子中的接口包含一个索引this、一个事件伊夫(Eve)n、一个方法Find和一个特性Point。

  接口可以帮忙多重继承。就像在下例中,接口”IComboBox”同时从”ITextBox”和”IListBox”继承。

 

interface IControl {
void Paint( ) ;
}
interface ITextBox: IControl {
void SetText(string text) ;
}
interface IListBox: IControl {
void SetItems(string[] items) ;
}
interface IComboBox: ITextBox, IListBox { } 

 

  类和结构可以多重实例化接口。就像在下例中,类”EditBox”继承了类”Control”,同时从”IDataBound”和”IControl”继承。

 

interface IDataBound {
 void Bind(Binder b) ;
}
public class EditBox: Control, IControl, IDataBound {
 public void Paint( ) ;
 public void Bind(Binder b) {…}

 

  在下边的代码中,”Paint”方法从”IControl”接口而来;”Bind”方法从”IDataBound”接口而来,皆以”public”的身价在”EditBox”类中实现。

  说明:

  1、C#中的接口是单独于类来定义的。这与 C++模型是相对的,在
C++中接口实际上就是空洞基类。

  2、接口和类都可以持续三个接口。

  3、而类可以继承一个基类,接口根本不可以继承类。这种模型制止了
C++的多继承问题,C++中不同基类中的实现可能出现争论。因而也不再需要诸如虚拟继承和显式功能域这类复杂机制。C#的简化接口模型有助于加速应用程序的付出。

  4、一个接口定义一个唯有空虚成员的引用类型。C#中一个接口实际所做的,仅仅只存在着艺术标明,但平生就从未履行代码。这就暗示了不可以实例化一个接口,只可以实例化一个派生自该接口的靶子。

  5、接口可以定义方法、属性和目录。所以,比较一个类,接口的特殊性是:当定义一个类时,可以派生自多重接口,而你只好可以从仅有的一个类派生。

       接口与组件

  接口描述了组件对外提供的劳务。在组件和零部件之间、组件和客户之间都经过接口举办交互。由此组件一旦宣布,它不得不通过先行定义的接口来提供客观的、一致的服务。这种接口定义之间的平稳使客户采纳开发者可以社团出稳步的运用。一个零件可以实现多个零件接口,而一个特定的零部件接口也足以被两个零件来实现。

  组件接口必须是力所能及自我描述的。这表示组件接口应该不借助于现实的落实,将促成和接口分离彻底消除了接口的使用者和接口的实现者之间的耦合关系,增强了音信的包裹程度。同时这也要求组件接口必须利用一种与组件实现无关的语言。方今组件接口的叙述标准是IDL语言。

  由于接口是组件之间的情商,由此组件的接口一旦被宣布,组件生产者就应有尽量地保障接口不变,任何对接口语法或语义上的改动,都有可能造成现有组件与客户之间的维系遭到破坏。

  每个组件都是自主的,有其特其余效应,只好通过接口与外场通信。当一个组件需要提供新的服务时,可以经过扩大新的接口来实现。不会影响原接口已存在的客户。而新的客户可以另行接纳新的接口来得到劳动。

  组件化程序设计

  组件化程序设计方法继承并升华了面向对象的顺序设计艺术。它把对象技术使用于系统规划,对面向对象的主次设计的贯彻过程作了一发的虚幻。我们得以把组件化程序设计艺术用作构造系统的系统布局层次的措施,并且可以行使面向对象的艺术很方便地落实组件。

  组件化程序设计强调真正的软件可重用性和惊人的互操作性。它注重于组件的发生和装配,这两上边一起组成了组件化程序设计的骨干。组件的发生过程不仅是采纳系统的急需,组件市场自我也有助于了组件的发展,促进了软件厂商的互换与协作。组件的装配使得软件出品得以动用类似于搭积木的点子飞速地确立起来,不仅可以裁减软件出品的开发周期,同时也增强了系统的平安和可靠性。

  组件程序设计的格局有以下多少个方面的风味:

  1、编程语言和付出条件的独立性;

  2、组件地点的透明性;

  3、组件的进程透明性;

  4、可扩展性;

  5、可重用性;

  6、具有强大的根基设备;

  7、系统一流的公共服务;

  C#语言由于其众多独到之处,分外适用于组件编程。但那并不是说C#是一门组件编程语言,也不是说C#提供了组件编程的工具。大家早就三番五遍提议,组件应该享有与编程语言无关的特色。请读者切记那或多或少:组件模型是一种标准,不管接纳何种程序语言设计组件,都无法不服从这一专业。比如组装电脑的例子,只要逐项厂商为大家提供的零配件规格、接口符合统一的业内,这多少个配件组合起来就能协同工作,组件编程也是一模一样。我们只是说,利用C#言语举行零部件编程将会给我们带来更大的造福。

  知道了什么是接口,接下去就是什么定义接口,请看下一节–定义接口。

  第二节 定义接口

  从技术上讲,接口是一组包含了函数型方法的数据结构。通过这组数据结构,客户代码可以调用组件对象的功力。

  定义接口的形似情势为:

[attributes] [modifiers] interface identifier [:base-list]
{interface-body}[;]

  说明:
       1、attributes(可选):附加的定义性音讯。

  2、modifiers(可选): 允许利用的修饰符有 new
和多个访问修饰符。分别是:new、public、protected、internal、
private。在一个接口定义中一样修饰符不同意出现多次,new
修饰符只可以出现在嵌套接口中,表示覆盖了连续而来的同名成员。The public,
protected, internal, and private 修饰符定义了对接口的访问权限。

  3、指示器和事件。

  4、identifier:接口名称。

  5、base-list(可选):包含一个或三个显式基接口的列表,接口间由逗号分隔。

  6、interface-body:对接口成员的定义。

  7、接口可以是命名空间或类的分子,并且可以分包下列成员的签约:
方法、属性、索引器 。

  8、一个接口可从一个或多少个基接口继承。

       
接口这些概念在C#和Java中特别相像。接口的要紧词是interface,一个接口可以扩大一个仍旧六个其他接口。按照规矩,接口的名字以大写字母”I”先导。下面的代码是C#接口的一个例子,它与Java中的接口完全一致:

 

interface IShape {
 void Draw ( ) ;
}

  如若您从六个或者五个以上的接口派生,父接口的名字列表用逗号分隔,如上边的代码所示:

interface INewInterface: IParent1, IParent2 { } 

  然而,与Java不同,C#中的接口不可能包含域(Field)。另外还要小心,在C#中,接口内的拥有办法默认都是公用方法。在Java中,方法定义可以蕴涵public修饰符(即便这不要必要),但在C#中,显式为接口的不二法门指定public修饰符是非法的。例如,上面的C#接口将生出一个编译错误。

interface IShape { public void Draw( ) ; }

  上面的例子定义了一个名为IControl
的接口,接口中隐含一个分子方法Paint:

interface IControl {
 void Paint( ) ;

  在下例中,接口 IInterface从多少个基接口 IBase1 和 IBase2 连续:

interface IInterface: IBase1, IBase2 {
 void Method1( ) ;
 void Method2( ) ;

  接口可由类实现。实现的接口的标识符出现在类的基列表中。例如:

class Class1: Iface1, Iface2 {
 // class 成员。
}

  类的基列表同时含有基类和接口时,列表中率先出现的是基类。例如:

class ClassA: BaseClass, Iface1, Iface2 {
 // class成员。
}

  以下的代码段定义接口IFace,它只有一个主意:

interface IFace {
 void ShowMyFace( ) ;
}

  无法从这多少个概念实例化一个目标,但足以从它派生一个类。因而,该类必须兑现ShowMyFace抽象方法:

class CFace:IFace
{
 public void ShowMyFace( ) {
  Console.WriteLine(” implementation ” ) ;
 }

基接口

  一个接口可以从零或两个接口继承,这一个被叫做那个接口的显式基接口。当一个接口有比零多的显式基接口时,那么在接口的定义中的格局为,接口标识符前边随着由一个冒号”:”和一个用逗号”,”分开的基接口标识符列表。

  接口基:

  :接口类型列表表明:

  1、一个接口的显式基接口必须至少同接口本身一样可访问。例如,在一个国有接口的基接口中指定一个私家或内部的接口是荒谬的。

  2、一个接口直接或直接地从它和谐继续是张冠李戴的。

  3、接口的基接口都是显式基接口,并且是它们的基接口。换句话说,基接口的聚众完全由显式基接口和它们的显式基接口等等组成。在下面的例子中
interface IControl {
 void Paint( ) ;
}
interface ITextBox: IControl {
 void SetText(string text) ;
}
interface IListBox: IControl {
 void SetItems(string[] items) ;
}
interface IComboBox: ITextBox, IListBox { }

  IComboBox 的基接口是IControl, ITextBox, 和 IlistBox。

  4、一个接口继承它的基接口的有着成员。换句话说,下边的接口 IComboBox
就像Paint一样持续成员SetText 和 SetItems。

  5、一个落实了接口的类或结构也隐含地实现了颇具接口的基接口。

  接口主体

  一个接口的接口主体定义接口的分子。

interface-body:
{ interface-member-declarationsopt }

  定义接口紧如若概念接口成员,请看下一节–定义接口成员。

其三节 定义接口成员

  接口可以蕴涵一个和四个成员,这个成员可以是方法、属性、索引提示器和事件,但无法是常量、域、操作符、构造函数或析构函数,而且不可能包含其他静态成员。接口定义创立新的定义空间,并且接口定义直接包含的接口成员定义将新成员引入该定义空间。

  说明:
       1、接口的积极分子是从基接口继承的积极分子和由接口本身定义的成员。

  2、接口定义可以定义零个或六个分子。接口的积极分子必须是方法、属性、事件或索引器。接口不可以包含常数、字段、运算符、实例构造函数、析构函数或项目,也无法包含其他项目标静态成员。

  3、定义一个接口,该接口对于每种可能体系的成员都包含一个:方法、属性、事件和索引器。

  4、接口成员默认访问格局是public。接口成员定义无法包含其他修饰符,比如成员定义前无法加abstract,public,protected,internal,private,virtual,override
或static 修饰符。

      
5、接口的成员之间不可能相互同名。继承而来的积极分子不用再定义,但接口可以定义与继承而来的分子同名的分子,这时咱们说接口成员覆盖了继续而来的分子,这不会造成错误,但编译器会提交一个警戒。关闭警告提示的情势是在成员定义前增长一个new关键字。但假如没有遮盖父接口中的成员,使用new
关键字会导致编译器发出警告。

  6、方法的名号必须与同一接口中定义的装有属性和事件的称谓不同。其余,方法的签名必须与同一接口中定义的所有其他艺术的签约不同。

  7、属性或事件的名目必须与同样接口中定义的有着其他成员的称呼不同。

  8、一个索引器的签字必须分别于在同等接口中定义的其它所有索引器的签署。

  9、接口方法表明中的属性(attributes), 再次来到类型(return-type),
标识符(identifier),
和方式参数列表(formal-parameter-lis)与一个类的措施声明中的这么些有一样的意思。一个接口方法表明不允许指定一个方法主体,而阐前几日常用一个分行截止。

  10、接口属性注脚的走访符与类属性讲明的走访符相对应,除了访问符主体经常必须用分号。因而,无论属性是读写、只读或只写,访问符都完全确定。

  11、接口索引注解中的属性(attributes), 类型(type), 和样式参数列表
(formal-parameter-list)与类的目录注明的这个有同一的意思。

  下边例子中接口IMyTest包含了目录指示器、事件E、 方法F、 属性P
这多少个成员:

 

interface IMyTest{
 string this[int index] { get; set; }
 event EventHandler E ;
 void F(int value) ;
 string P { get; set; }
}
public delegate void EventHandler(object sender, EventArgs e) ;

 

  下边例子中接口IStringList包含每个可能类型成员的接口:一个艺术,一个特性,一个事变和一个索引。

 

public delegate void StringListEvent(IStringList sender);
public interface IStringList
{
 void Add(string s);
 int Count { get; }
 event StringListEvent Changed;
 string this[int index] { get; set; }
}

 

  接口成员的全权名

  使用接口成员也可应用全权名(fully qualified
name)。接口的全权名称是这般组合的。接口名加小圆点”.”
再跟成员名比如对于下面多个接口:

 

interface IControl {
 void Paint( ) ;
}
interface ITextBox: IControl {
 void GetText(string text) ;
}

 

  其中Paint 的全权名是IControl.Paint,GetText的全权名是ITextBox.
GetText。当然,全权名中的成员名称必须是在接口中曾经定义过的,比如利用ITextBox.Paint.就是不客观的。

  假如接口是名字空间的分子,全权名还非得含出名字空间的名号。

 

namespace System
{
 public interface IDataTable {
  object Clone( ) ;
 }
}

 

  那么Clone方法的全权名是System. IDataTable.Clone。

  定义好了接口,接下去就是什么访问接口,请看下一节–访问接口

第四节、访问接口

  对接口成员的访问

  对接口方法的调用和采取索引指示器访问的平整与类中的情状也是一律的。倘诺底层成员的命名与后续而来的高层成员一致,那么底层成员将覆盖同名的高层成员。但出于接口辅助多延续,在多延续中,如若多少个父接口含有同名的分子,这就发出了二义性(这也正是C#中裁撤了类的多继承机制的原故之一),这时急需举行显式的概念:

 

using System ;
interface ISequence {
 int Count { get; set; }
}
interface IRing {
 void Count(int i) ;
}
interface IRingSequence: ISequence, IRing { }
 class CTest {
  void Test(IRingSequence rs) {
   //rs.Count(1) ; 错误, Count 有二义性
   //rs.Count = 1; 错误, Count 有二义性
   ((ISequence)rs).Count = 1; // 正确
   ((IRing)rs).Count(1) ; // 正确调用IRing.Count
  }
}

 

  下面的例子中,前两条语句rs .Count(1)和rs .Count =
1会生出二义性,从而导致编译时不当,由此必须显式地给rs
指派父接口类型,这种指派在运作时不会带来额外的支付。

  再看下面的例证:

 

using System ;
interface IInteger {
 void Add(int i) ;
}
interface IDouble {
 void Add(double d) ;
}
interface INumber: IInteger, IDouble {}
 class CMyTest {
 void Test(INumber Num) {
  // Num.Add(1) ; 错误
  Num.Add(1.0) ; // 正确
  ((IInteger)n).Add(1) ; // 正确
  ((IDouble)n).Add(1) ; // 正确
 }
}

 

  调用Num.Add(1)
会导致二义性,因为候选的重载方法的参数类型均适用。但是,调用Num.Add(1.0)
是同意的,因为1.0
是浮点数参数类型与措施IInteger.Add()的参数类型不一样,这时唯有IDouble.Add
才是适用的。然则只要进入了显式的差使,就无须会发出二义性。

  接口的一连串继承的题目也会带来成员访问上的问题。例如:

 

interface IBase {
 void FWay(int i) ;
}
interface ILeft: IBase {
 new void FWay (int i) ;
}
interface IRight: IBase
{ void G( ) ; }
interface IDerived: ILeft, IRight { }
class CTest {
 void Test(IDerived d) {
  d. FWay (1) ; // 调用ILeft. FWay
  ((IBase)d). FWay (1) ; // 调用IBase. FWay
  ((ILeft)d). FWay (1) ; // 调用ILeft. FWay
  ((IRight)d). FWay (1) ; // 调用IBase. FWay
 }
}

 

  上例中,方法IBase.FWay在派生的接口ILeft中被Ileft的积极分子方法FWay覆盖了。所以对d.
FWay (1)的调用实际上调用了。尽管从IBase-> IRight->
IDerived这条继承路径上来看,ILeft.FWay方法是从未有过被遮盖的。我们假如记住这或多或少:一旦成员被掩盖未来,所有对其的走访都被覆盖未来的成员”拦截”了。

  类对接口的兑现

  前面我们曾经说过,接口定义不包括方法的贯彻部分。接口可以因此类或社团来落实。大家根本讲述通过类来促成接口。用类来实现接口时,接口的名目必须含有在类定义中的基类列表中。

  下面的例子给出了由类来兑现接口的例证。其中ISequence
为一个系列接口,提供了向队列尾部添加对象的分子方法Add( ),IRing
为一个循环表接口,提供了向环中插入对象的法门Insert(object
obj),方法再次回到插入的职位。类RingSquence 实现了接口ISequence
和接口IRing。

 

using System ;
interface ISequence {
 object Add( ) ;
}
interface ISequence {
 object Add( ) ;
}
interface IRing {
 int Insert(object obj) ;
}
class RingSequence: ISequence, IRing
{
 public object Add( ) {…}
 public int Insert(object obj) {…}
}

 

  如若类实现了某个接口,类也隐式地延续了该接口的具备父接口,不管那一个父接口有没有在类定义的基类表中列出。看下边的例证:

 

using System ;
interface IControl {
 void Paint( );
}
interface ITextBox: IControl {
 void SetText(string text);
}
interface IListBox: IControl {
 void SetItems(string[] items);
}
interface IComboBox: ITextBox, IListBox { }

 

  这里,
接口IcomboBox继承了ItextBox和IlistBox。类TextBox不仅实现了接口ITextBox,还实现了接口ITextBox
的父接口IControl。

  前边大家早就阅览,一个类能够实现多少个接口。再看下边的例证:

 

interface IDataBound {
 void Bind(Binder b);
}
public class EditBox: Control, IControl, IDataBound {
 public void Paint( );
 public void Bind(Binder b) {…}

 

  类EditBox从类Control中派生并且实现了Icontrol和IdataBound。在前方的例子中接口Icontrol中的Paint方法和IdataBound接口中的Bind方法都用类EditBox中的公共成员贯彻。C#提供一种实现这些情势的可挑选的路线,这样可以使执行那一个的类制止把那一个成员设定为集体的。接口成员能够用有效的称号来实现。例如,类EditBox可以改作方法Icontrol.Paint和IdataBound.Bind来来实现。

 

public class EditBox: IControl, IDataBound {
 void IControl.Paint( ) {…}
 void IDataBound.Bind(Binder b) {…}
}

 

  因为通过外部指派接口成员贯彻了每个成员,所以用这种形式实现的成员称为外部接口成员。外部接口成员可以只是经过接口来调用。例如,Paint方法中EditBox的贯彻可以只是由此创造Icontrol接口来调用。

 

class Test {
 static void Main( ) {
  EditBox editbox = new EditBox( );
  editbox.Paint( ); //错误: EditBox 没有Paint 事件
  IControl control = editbox;
  control.Paint( ); // 调用 EditBox的Paint事件
 }
}

 

  上例中,类EditBox 从Control 类继承并同时实现了IControl and
IDataBound 接口。EditBox 中的Paint 方法来自IControl 接口,Bind
方法来自IDataBound 接口,二者在EditBox
类中都看做国有成员贯彻。当然,在C#
中我们也足以选拔不作为公有成员贯彻接口。

  倘诺每个成员都强烈地提议了被实现的接口,通过这种路径被实现的接口大家称为显式接口成员(explicit
interface member)。 用这种方法大家改写上边的事例:

 

public class EditBox: IControl, IDataBound {
 void IControl.Paint( ) {…}
 void IDataBound.Bind(Binder b) {…}
}

 

  显式接口成员只好通过接口调用。例如:

 

class CTest {
 static void Main( ) {
  EditBox editbox = new EditBox( ) ;
  editbox.Paint( ) ; //错误:不同的点子
  IControl control = editbox;
  control.Paint( ) ; //调用 EditBox的Paint方法
 }
}

 

  上述代码中对editbox.Paint( )的调用是谬误的,因为editbox
本身并不曾提供这一办法。control.Paint( )是无可非议的调用格局。

  注释:接口本身不提供所定义的成员的实现,它不过表达这几个分子,这一个分子必须借助实现接口的类或任何接口的支撑。

  知道了咋样访问接口,我们还要明白怎么实现接口,要兑现C#的接口,请看下一节-实现接口

第五节、实现接口

  1、显式实现接口成员

  为了实现接口,类可以定义显式接口成员执行体(Explicit interface
member
implementations)。显式接口成员执行体可以是一个形式、一个特性、一个风波或者是一个目录指示器的概念,定义与该成员对应的全权名应保持一致。

 

using System ;
interface ICloneable {
 object Clone( ) ;
}
interface IComparable {
 int CompareTo(object other) ;
}
class ListEntry: ICloneable, IComparable {
 object ICloneable.Clone( ) {…}
 int IComparable.CompareTo(object other) {…}
}

 

  上边的代码中ICloneable.Clone 和IComparable.CompareTo
就是显式接口成员执行体。

       说明:

  1、不可能在章程调用、属性访问以及索引指示器访问中经过全权名访问显式接口成员执行体。事实上,显式接口成员执行体只可以通过接口的实例,仅仅引用接口的成员名称来访问。

  2、显式接口成员执行体不可能使用另外访问限制符,也不能够加上abstract,
virtual, override或static 修饰符。

  3、显式接口成员执行体和任何成员具有不同的访问模式。因为不可以在点子调用、属性访问以及索引提示器访问中经过全权名访问,显式接口成员执行体在某种意义上是个体的。但它们又足以由此接口的实例访问,也不无自然的国有性质。

  4、只有类在概念时,把接口名写在了基类列表中,而且类中定义的全权名、类型和重返类型都与显式接口成员执行体完全一致时,显式接口成员执行体才是实用的,例如:

 

class Shape: ICloneable {
object ICloneable.Clone( ) {…}
int IComparable.CompareTo(object other) {…}
}

 

运用显式接口成员执行体经常有三个目的:

  1、因为显式接口成员执行体不可以经过类的实例举办访问,这就可以从国有接口中把接口的实现部分单独分离开。假使一个类只在内部采取该接口,而类的使用者不会直接动用到该接口,这种显式接口成员执行体就可以起到职能。

  2、显式接口成员执行体防止了接口成员之内因为同名而发出混淆。倘使一个类希望对名称和再次来到类型相同的接口成员采用不同的实现情势,那就必须要使用到显式接口成员执行体。要是没有显式接口成员执行体,那么对于名称和重回类型不同的接口成员,类也无能为力举行落实。

  下面的概念是低效的,因为Shape
定义时基类列表中没有出现接口IComparable。

 

class Shape: ICloneable
{
object ICloneable.Clone( ) {…}
}
class Ellipse: Shape
{
object ICloneable.Clone( ) {…}
}

 

  在Ellipse
中定义ICloneable.Clone是不当的,因为Ellipse尽管隐式地落实了接口ICloneable,ICloneable依旧没有显式地面世在Ellipse定义的基类列表中。

  接口成员的全权名必须对应在接口中定义的积极分子。如上面的事例中,Paint的显式接口成员执行体必须写成IControl.Paint。

 

using System ;
interface IControl
{
 void Paint( ) ;
}
interface ITextBox: IControl
{
 void SetText(string text) ;
}
class TextBox: ITextBox
{
 void IControl.Paint( ) {…}
 void ITextBox.SetText(string text) {…}
}
 

 

  实现接口的类可以显式实现该接口的成员。当显式实现某成员时,不可能因此类实例访问该成员,而只好通过该接口的实例访问该成员。显式接口实现还同意程序员继承共享相同成员名的两个接口,并为每个接口成员提供一个独门的落实。

  上面例子中并且以公制单位和英制单位显示框的尺码。Box类继承
IEnglishDimensions和
IMetricDimensions多少个接口,它们表示不同的度量衡系统。六个接口有同等的分子名
Length 和 Width。

  程序清单1 DemonInterface.cs

 

interface IEnglishDimensions {
float Length ( ) ;
float Width ( ) ;
}
interface IMetricDimensions {
float Length ( ) ;
float Width ( ) ;
}
class Box : IEnglishDimensions, IMetricDimensions {
float lengthInches ;
float widthInches ;
public Box(float length, float width) {
lengthInches = length ;
widthInches = width ;
}
float IEnglishDimensions.Length( ) {
return lengthInches ;
}
float IEnglishDimensions.Width( ) {
return widthInches ;
}
float IMetricDimensions.Length( ) {
return lengthInches * 2.54f ;
}
float IMetricDimensions.Width( ) {
return widthInches * 2.54f ;
}
public static void Main( ) {
//定义一个实类对象 “myBox”::
Box myBox = new Box(30.0f, 20.0f);
// 定义一个接口” eDimensions”::
IEnglishDimensions eDimensions = (IEnglishDimensions) myBox;
IMetricDimensions mDimensions = (IMetricDimensions) myBox;
// 输出:
System.Console.WriteLine(” Length(in): {0}”, eDimensions.Length( ));
System.Console.WriteLine(” Width (in): {0}”, eDimensions.Width( ));
System.Console.WriteLine(” Length(cm): {0}”, mDimensions.Length( ));
System.Console.WriteLine(” Width (cm): {0}”, mDimensions.Width( ));
}
}

 

  输出:Length(in): 30,Width (in): 20,Length(cm): 76.2,Width (cm):
50.8

  代码研商:假诺希望默认度量拔取英制单位,请正常实现 Length 和 Width
这五个法子,并从 IMetricDimensions 接口显式实现 Length 和 Width 方法:

 

public float Length( ) {
return lengthInches ;
}
public float Width( ){
return widthInches;
}
float IMetricDimensions.Length( ) {
return lengthInches * 2.54f ;
}
float IMetricDimensions.Width( ) {
return widthInches * 2.54f ;
}

 

  这种状态下,能够从类实例访问英制单位,而从接口实例访问公制单位:

 

System.Console.WriteLine(“Length(in): {0}”, myBox.Length( )) ;
System.Console.WriteLine(“Width (in): {0}”, myBox.Width( )) ;
System.Console.WriteLine(“Length(cm): {0}”, mDimensions.Length( )) ;
System.Console.WriteLine(“Width (cm): {0}”, mDimensions.Width( )) ;

 

  4、映射接口

  类必须为在基类表中列出的拥有接口的分子提供具体的兑现。
在类中稳定接口成员的实现称之为接口映射(interface mapping )。

  映射,数学上意味着一一对应的函数关系。接口映射的意义也是千篇一律,接口通过类来落实,
那么对于在接口中定义的每一个分子,都应当相应着类的一个成员来为它提供切实的贯彻。

  类的分子及其所映射的接口成员之内必须满意下列原则:

  1、假诺A和B都是成员方法,那么A和B的称号、类型、形参表(包括参数个数和每一个参数的项目)都应有是相同的。

  2、如若A和B都是性质,那么A和B的称呼、类型应当一律,而且A和B的访问器也是近似的。但即便A不是显式接口成员执行体,A允许增添自己的访问器。

  3、假诺A和B都是光阴那么A和B的称呼、类型应当平等。

  4、假使A和B都是索引提示器,那么A和B的类型、形参表(包括参数个数和每一个参数的项目)应当平等。而且A和B的访问器也是看似的。但一旦A不是显式接口成员执行体,A允许扩大和谐的访问器。

  那么,对于一个接口成员,咋样确定由哪一个类的积极分子来促成啊?即一个接口成员映射的是哪一个类的成员?在这边,我们讲述一下接口映射的经过。倘若类C实现了一个接口IInterface,Member是接口IInterface中的一个成员,在稳住由谁来实现接口成员Member,即Member的映射过程是这样的:

  1、假如C中存在着一个显式接口成员执行体,该执行体与接口IInterface
及其成员Member绝对应,则由它来兑现Member 成员。

  2、如若条件(1)不知足,且C中存在着一个非静态的国有成员,该成员与接口成员Member相对应,则由它来兑现Member
成员。

  3、如若上述条件仍不满意,则在类C定义的基类列表中搜寻一个C
的基类D,用D来代替C。

  4、重复步骤1– 3
,遍历C的具有直接基类和非直接基类,直到找到一个满意条件的类的积极分子。

  5、如若依旧没有找到,则告知错误。

  上面是一个调用基类方法来落实接口成员的事例。类Class2
实现了接口Interface1,类Class2 的基类Class1
的积极分子也涉足了接口的映射,也就是说类Class2
在对接口Interface1举办落实时,使用了类Class1提供的分子方法F来贯彻接口Interface1的积极分子方法F:

 

 

1、显式实现接口成员

为了落实接口,类可以定义显式接口成员实施体(Explicit interface member
implementations)。显式接口成员执行体能够是一个办法、一个特性、一个事变或者是一个目录提示器的定义,定义与该成员对应的全权名应保持一致。

using System ;
interface ICloneable {
object Clone( ) ;
}
interface IComparable {
int CompareTo(object other) ;
}
class ListEntry: ICloneable, IComparable {
object ICloneable.Clone( ) {…}
int IComparable.CompareTo(object other) {…}
}

上边的代码中ICloneable.Clone 和IComparable.CompareTo
就是显式接口成员执行体。

说明:

1、无法在点子调用、属性访问以及索引指示器访问中经过全权名访问显式接口成员执行体。事实上,显式接口成员执行体只好通过接口的实例,仅仅引用接口的分子名称来做客。

2、显式接口成员执行体不能够使用此外访问限制符,也无法加上abstract,
virtual, override或static 修饰符。

3、显式接口成员执行体和另外成员具有不同的造访形式。因为无法在形式调用、属性访问以及索引提示器访问中通过全权名访问,显式接口成员执行体在某种意义上是个人的。但它们又可以因而接口的实例访问,也兼具自然的公有性质。

4、唯有类在概念时,把接口名写在了基类列表中,而且类中定义的全权名、类型和重临类型都与显式接口成员执行体完全一致时,显式接口成员执行体才是立竿见影的,例如:

class Shape: ICloneable {
object ICloneable.Clone( ) {…}
int IComparable.CompareTo(object other) {…}
}

动用显式接口成员执行体平常有多少个目标:

1、因为显式接口成员执行体不可以由此类的实例举行走访,这就可以从国有接口中把接口的兑现部分单独分离开。即便一个类只在中间使用该接口,而类的使用者不会平素动用到该接口,这种显式接口成员执行体就可以起到职能。

2、显式接口成员执行体制止了接口成员之间因为同名而暴发混淆。假使一个类希望对名称和重回类型相同的接口成员使用不同的兑现模式,这就必须要使用到显式接口成员执行体。要是没有显式接口成员执行体,那么对于名称和重临类型不同的接口成员,类也无所适从进展落实。

下面的概念是无用的,因为Shape 定义时基类列表中绝非出现接口IComparable。

class Shape: ICloneable
{
object ICloneable.Clone( ) {…}
}
class Ellipse: Shape
{
object ICloneable.Clone( ) {…}
}

在Ellipse
中定义ICloneable.Clone是漏洞百出的,因为Ellipse即使隐式地贯彻了接口ICloneable,ICloneable仍旧没有显式地涌出在Ellipse定义的基类列表中。

接口成员的全权名必须对应在接口中定义的积极分子。如下边的例证中,Paint的显式接口成员执行体必须写成IControl.Paint。

using System ;
interface IControl
{
void Paint( ) ;
}
interface ITextBox: IControl
{
void SetText(string text) ;
}
class TextBox: ITextBox
{
void IControl.Paint( ) {…}
void ITextBox.SetText(string text) {…}
}

心想事成接口的类可以显式实现该接口的积极分子。当显式实现某成员时,不可以经过类实例访问该成员,而只好通过该接口的实例访问该成员。显式接口实现还同意程序员继承共享相同成员名的两个接口,并为每个接口成员提供一个独自的兑现。

下面例子中还要以公制单位和英制单位出示框的尺码。Box类继承
IEnglishDimensions和
IMetricDimensions五个接口,它们表示不同的度量衡系统。五个接口有一样的积极分子名
Length 和 Width。

程序清单1 DemonInterface.cs

interface IEnglishDimensions {
float Length ( ) ;
float Width ( ) ;
}
interface IMetricDimensions {
float Length ( ) ;
float Width ( ) ;
}
class Box : IEnglishDimensions, IMetricDimensions {
float lengthInches ;
float widthInches ;
public Box(float length, float width) {
lengthInches = length ;
widthInches = width ;
}
float IEnglishDimensions.Length( ) {
return lengthInches ;
}
float IEnglishDimensions.Width( ) {
return widthInches ;
}
float IMetricDimensions.Length( ) {
return lengthInches * 2.54f ;
}
float IMetricDimensions.Width( ) {
return widthInches * 2.54f ;
}
public static void Main( ) {
//定义一个实类对象 “myBox”::
Box myBox = new Box(30.0f, 20.0f);
// 定义一个接口” eDimensions”::
IEnglishDimensions eDimensions = (IEnglishDimensions) myBox;
IMetricDimensions mDimensions = (IMetricDimensions) myBox;
// 输出:
System.Console.WriteLine(” Length(in): {0}”, eDimensions.Length( ));
System.Console.WriteLine(” Width (in): {0}”, eDimensions.Width( ));
System.Console.WriteLine(” Length(cm): {0}”, mDimensions.Length( ));
System.Console.WriteLine(” Width (cm): {0}”, mDimensions.Width( ));
}
}

输出:Length(in): 30,Width (in): 20,Length(cm): 76.2,Width (cm): 50.8

代码研究:如果期望默认度量拔取英制单位,请正常实现 Length 和 Width
这五个法子,并从 IMetricDimensions 接口显式实现 Length 和 Width 方法:

public float Length( ) {
return lengthInches ;
}
public float Width( ){
return widthInches;
}
float IMetricDimensions.Length( ) {
return lengthInches * 2.54f ;
}
float IMetricDimensions.Width( ) {
return widthInches * 2.54f ;
}

这种情状下,可以从类实例访问英制单位,而从接口实例访问公制单位:

System.Console.WriteLine(“Length(in): {0}”, myBox.Length( )) ;
System.Console.WriteLine(“Width (in): {0}”, myBox.Width( )) ;
System.Console.WriteLine(“Length(cm): {0}”, mDimensions.Length( )) ;
System.Console.WriteLine(“Width (cm): {0}”, mDimensions.Width( )) ;

2、继承接口实现

接口具有不变性,但这并不意味接口不再发展。类似于类的继承性,接口也可以继续和提高。

留意:接口继承和类继承不同,首先,类继承不仅是认证继承,而且也是促成连续;而接口继承只是表达继承。也就是说,派生类可以连续基类的法门实现,而
派生的接口只持续了父接口的分子方法求证,而尚未持续父接口的兑现,其次,C#中类继承只同意单继承,但是接口继承允许多卫冕,一个子接口可以有六个父接
口。

接口可以从零或多少个接口中持续。从两个接口中持续时,用”:”后跟被连续的接口名字,多少个接口名之间用”,”分割。被延续的接口应该是可以访问取得
的,比如从private 类型或internal
类型的接口中继承就是不允许的。接口不允许直接或直接地从自家继承。和类的接轨相似,接口的存续也形成接口之间的层次结构。

请看下边的例证:

using System ;
interface IControl {
void Paint( ) ;
}
interface ITextBox: IControl {
void SetText(string text) ;
}
interface IListBox: IControl {
void SetItems(string[] items) ;
}
interface IComboBox: ITextBox, IListBox { }

对一个接口的接轨也就持续了接口的兼具成员,上边的事例中接口ITextBox和IListBox都从接口IControl中连续,也就连续了接口
IControl的Paint方法。接口IComboBox从接口ITextBox和IListBox中继承,因而它应有继承了接口ITextBox的
SetText方法和IListBox的SetItems方法,还有IControl的Paint方法。
一个类继承了装有被它的基本类提供的接口实现程序。

不经过显式的贯彻一个接口,一个派生类不可能用任何格局改变它从它的基本类继承的接口映射。例如,在宣称中

interface IControl {
void Paint( );
}
class Control: IControl {
public void Paint( ) {…}
}
class TextBox: Control {
new public void Paint( ) {…}
}

TextBox 中的方法Paint 隐藏了Control中的方法Paint
,可是并未改动从Control.Paint 到IControl.Paint
的照耀,而通过类实例和接口实例调用Paint将会有下面的影响

Control c = new Control( ) ;
TextBox t = new TextBox( ) ;
IControl ic = c ;
IControl it = t ;
c.Paint( ) ; // 影响Control.Paint( ) ;
t.Paint( ) ; // 影响TextBox.Paint( ) ;
ic.Paint( ) ; // 影响Control.Paint( ) ;
it.Paint( ) ; // 影响Control.Paint( ) ;

唯独,当一个接口方法被映射到一个类中的虚拟方法,派生类就不容许覆盖那一个编造方法并且改变接口的落实函数。例如,把下边的宣示重新写为

interface IControl {
void Paint( ) ;
}
class Control: IControl {
public virtual void Paint( ) {…}
}
class TextBox: Control {
public override void Paint( ) {…}
}

就会看到下边的结果:

Control c = new Control( ) ;
TextBox t = new TextBox( ) ;
IControl ic = c ;
IControl it = t ;
c.Paint( ) ; // 影响Control.Paint( );
t.Paint( ) ; // 影响TextBox.Paint( );
ic.Paint( ) ; // 影响Control.Paint( );
it.Paint( ) ; // 影响TextBox.Paint( );

鉴于显式接口成员贯彻程序不可能被声称为虚拟的,就无法覆盖一个显式接口成员贯彻程序。一个显式接口成员贯彻程序调用另外一个艺术是卓有效用的,而另外的不得了形式可以被声称为虚构的以便让派生类可以覆盖它。例如:

interface IControl {
void Paint( ) ;
}
class Control: IControl {
void IControl.Paint( ) { PaintControl( ); }
protected virtual void PaintControl( ) {…}
}
class TextBox: Control {
protected override void PaintControl( ) {…}
}

这里,从Control 继承的类可以通过覆盖措施PaintControl 来对IControl.Paint
的兑现程序开展特殊化。

3、重新实现接口

我们曾经介绍过,派生类可以对基类中曾经定义的积极分子方法进行重载。类似的概念引入到类对接口的实现中来,叫做接口的重落实(re-
implementation)。继承了接口实现的类可以对接口举行重落实。这么些接口要求是在类定义的基类列表中冒出过的。对接口的重落实也非得从严地坚守第一次实现接口的规则,派生的接口映射不会对为接口的重落实所确立的接口映射暴发任何影响。

下面的代码给出了接口重落实的事例:

interface IControl {
void Paint( ) ;
class Control: IControl
void IControl.Paint( ) {…}
class MyControl: Control, IControl
public void Paint( ) {}
}

骨子里就是:Control把IControl.Paint映射到了Control.IControl.Paint上,但这并不影响在
MyControl中的重落实。在MyControl中的重落实中,IControl.Paint被映射到MyControl.Paint
之上。

在接口的重落实时,继承而来的国有成员定义和延续而来的显式接口成员的概念参加到接口映射的经过。

using System ;
interface IMethods {
void F( ) ;
void G( ) ;
void H( ) ;
void I( ) ;
}
class Base: IMethods {
void IMethods.F( ) { }
void IMethods.G( ) { }
public void H( ) { }
public void I( ) { }
}
class Derived: Base, IMethods {
public void F( ) { }
void IMethods.H( ) { }
}

此地,接口IMethods在Derived中的实现把接口方法映射到了Derived.F,Base.IMethods.G,
Derived.IMethods.H,
还有Base.I。后面我们说过,类在实现一个接口时,同时隐式地贯彻了该接口的保有父接口。同样,类在重落实一个接口时还要,隐式地重落实了该接口的所
有父接口。

using System ;
interface IBase {
void F( ) ;
}
interface IDerived: IBase {
void G( ) ;
}
class C: IDerived {
void IBase.F( ) {
//对F 举行落实的代码…
}
void IDerived.G( ) {
//对G 举办落实的代码…
}
}
class D: C, IDerived {
public void F( ) {
//对F 举行落实的代码…
}
public void G( ) {
//对G 举行落实的代码…
}
}

这边,对IDerived的重落实也一律实现了对IBase的重落实,把IBase.F
映射到了D.F。

4、映射接口

类必须为在基类表中列出的兼具接口的积极分子提供切实的实现。在类中定位接口成员的兑现称之为接口映射(interface
mapping )。

辉映,数学上代表一一对应的函数关系。接口映射的意思也是平等,接口通过类来贯彻,那么对于在接口中定义的每一个分子,都应当相应着类的一个成员来为它提供具体的落实。

类的积极分子及其所映射的接口成员之内必须满意下列条件:

1、即便A和B都是成员方法,那么A和B的称号、类型、形参表(包括参数个数和每一个参数的项目)都应有是相同的。

2、假设A和B都是性质,那么A和B的称谓、类型应当平等,而且A和B的访问器也是相仿的。但假设A不是显式接口成员执行体,A允许扩大和谐的访问器。

3、如若A和B都是岁月那么A和B的称谓、类型应当一律。

4、假使A和B都是索引提示器,那么A和B的花色、形参表(包括参数个数和每一个参数的序列)应当平等。而且A和B的访问器也是看似的。但如果A不是显式接口成员执行体,A允许扩充和谐的访问器。

那么,对于一个接口成员,怎么样确定由哪一个类的分子来贯彻呢?即一个接口成员映射的是哪一个类的成员?在此处,我们讲述一下接口映射的过程。倘诺类C
实现了一个接口IInterface,Member是接口IInterface中的一个成员,在稳住由何人来实现接口成员Member,即Member的映
射过程是这样的:

1、假若C中设有着一个显式接口成员执行体,该执行体与接口IInterface
及其成员Member相对应,则由它来促成Member 成员。

2、假若基准(1)不知足,且C中存在着一个非静态的公有成员,该成员与接口成员Member绝对应,则由它来兑现Member
成员。

3、倘使上述标准仍不满意,则在类C定义的基类列表中追寻一个C
的基类D,用D来代替C。

4、重复步骤1– 3
,遍历C的具有直接基类和非直接基类,直到找到一个满意条件的类的积极分子。

5、假使依然没有找到,则告知错误。

下边是一个调用基类方法来实现接口成员的事例。类Class2
实现了接口Interface1,类Class2 的基类Class1
的积极分子也出席了接口的映照,也就是说类Class2
在对接口Interface1进行落实时,使用了类Class1提供的成员方法F来贯彻接口Interface1的分子方法F:

interface Interface1 {
void F( ) ;
}
class Class1 {
public void F( ) { }
public void G( ) { }
}
class Class2: Class1, Interface1 {
new public void G( ) {}
}

留神:接口的成员包括它和谐定义的分子,而且包括该接口所有父接口定义的成员。在接口映射时,不仅要对接口定义体中显式定义的具有成员开展映射,而且要对隐式地从父接口这里继承来的具备接口成员举行映射。

在拓展接口映射时,还要小心下边两点:

1、在决定由类中的哪个成员来兑现接口成员时,类中显式表明的接口成员比此外成员优先实现。

2、使用Private、protected和static修饰符的分子无法参与实现接口映射。例如:

interface ICloneable {
object Clone( ) ;
}
class C: ICloneable {
object ICloneable.Clone( ) {…}
public object Clone( ) {…}
}

事例中成员ICloneable.Clone 称为接口ICloneable 的积极分子Clone
的实现者,因为它是显式表明的接口成员,比其它成员具有更高的优先权。

假设一个类实现了多少个或多个以上名字、类型和参数类型都如出一辙的接口,那么类中的一个分子就可能实现所有这个接口成员:

interface IControl {
void Paint( ) ;
}
interface IForm {
void Paint( ) ;
}
class Page: IControl, IForm {
public void Paint( ) {…}
}

这里,接口IControl和IForm的措施Paint都映射到了类Page中的Paint方法。当然也得以独家用显式的接口成员分头实现这多少个措施:

interface IControl {
void Paint( ) ;
}
interface IForm {
void Paint( ) ;
}
class Page: IControl, IForm {
public void IControl.Paint( ) {
//具体的接口实现代码
}
public void IForm.Paint( ) {
//具体的接口实现代码
}
}

上边的二种写法都是正确的。不过只要接口成员在此起彼伏中覆盖了父接口的成员,那么对该接口成员的兑现就可能必须映射到显式接口成员执行体。看下边的例子:

interface IBase {
int P { get; }
}
interface IDerived: IBase {
new int P( ) ;
}

接口IDerived从接口IBase中持续,这时接口IDerived
的积极分子方法覆盖了父接口的成员方法。因为这时候存在着同名的六个接口成员,那么对这多少个接口成员的贯彻假如不应用显式接口成员执行体,编译器将不可以甄别接口
映射。所以,假若某个类要促成接口IDerived,在类中必须至少定义一个显式接口成员执行体。选拔下边这些写法都是客观的:

//一:对四个接口成员都拔取显式接口成员执行体来实现
lass C: IDerived {
int IBase.P
get
{ //具体的接口实现代码 }
int IDerived.P( ){
//具体的接口实现代码 }
}
//二:对Ibase 的接口成员使用显式接口成员执行体来实现
class C: IDerived {
int IBase.P
get {//具体的接口实现代码}
public int P( ){
//具体的接口实现代码 }
}
//三:对IDerived 的接口成员运用显式接口成员执行体来实现
class C: IDerived{
public int P
get {//具体的接口实现代码}
int IDerived.P( ){
//具体的接口实现代码}
}

另一种情状是,如果一个类实现了五个接口,这个接口又怀有同一个父接口,这么些父接口只允许被实现两回。

using System ;
interface IControl {
void Paint( ) ;
interface ITextBox: IControl {
void SetText(string text) ;
}
interface IListBox: IControl {
void SetItems(string[] items) ;
}
class ComboBox: IControl, ITextBox, IListBox {
void IControl.Paint( ) {…}
void ITextBox.SetText(string text) {…}
void IListBox.SetItems(string[] items) {…}
}

下边的例子中,类ComboBox实现了两个接口:IControl,ITextBox和IListBox。倘使觉得ComboBox不仅实现了
IControl接口,而且在促成ITextBox和IListBox的还要,又分别实现了它们的父接口IControl。实际上,对接口
ITextBox 和IListBox 的兑现,分享了对接口IControl 的贯彻。

我们对C#的接口有了较系数的认识,基本精晓了哪些应用C#的接口编程,但事实上,C#的不仅应用于.net平台,它一律帮助以前的COM,可以兑现COM类到.NET类的更换,如C#调用API。欲了然这上头的学识,请看下一节-接口转换。
 
百度空间 | 百度首页 | 登录 想想再定 主页博客相册|个人档案 |好友   
查看随笔     
C#接口基础(6) 2009-07-30 16:44 第四节、访问接口

对接口成员的造访

对接口方法的调用和选拔索引指示器访问的条条框框与类中的处境也是千篇一律的。即便底层成员的命名与持续而来的高层成员一致,那么底层成员将覆盖同名的高层成
员。但由于接口辅助多延续,在多延续中,如若六个父接口含有同名的分子,这就时有暴发了二义性(那也正是C#中收回了类的多继承机制的由来之一),这时急需进行显式的概念:

 

using System ;
interface ISequence {
int Count { get; set; }
}
interface IRing {
void Count(int i) ;
}
interface IRingSequence: ISequence, IRing { }
class CTest {
void Test(IRingSequence rs) {
//rs.Count(1) ; 错误, Count 有二义性
//rs.Count = 1; 错误, Count 有二义性
((ISequence)rs).Count = 1; // 正确
((IRing)rs).Count(1) ; // 正确调用IRing.Count
}
}

地点的例证中,前两条语句rs .Count(1)和rs .Count =
1会暴发二义性,从而致使编译时不当,因而必须显式地给rs
指派父接口类型,这种指派在运作时不会带来额外的开发。

再看下面的例证:

using System ;
interface IInteger {
void Add(int i) ;
}
interface IDouble {
void Add(double d) ;
}
interface INumber: IInteger, IDouble {}
class CMyTest {
void Test(INumber Num) {
// Num.Add(1) ; 错误
Num.Add(1.0) ; // 正确
((IInteger)n).Add(1) ; // 正确
((IDouble)n).Add(1) ; // 正确
}
}

调用Num.Add(1)
会导致二义性,因为候选的重载方法的参数类型均适用。可是,调用Num.Add(1.0)
是允许的,因为1.0
是浮点数参数类型与艺术IInteger.Add()的参数类型不相同,这时唯有IDouble.Add
才是适用的。不过假若插手了显式的外派,就无须会发出二义性。

接口的多如牛毛继承的问题也会带动成员访问上的题材。例如:

interface IBase {
void FWay(int i) ;
}
interface ILeft: IBase {
new void FWay (int i) ;
}
interface IRight: IBase
{ void G( ) ; }
interface IDerived: ILeft, IRight { }
class CTest {
void Test(IDerived d) {
d. FWay (1) ; // 调用ILeft. FWay
((IBase)d). FWay (1) ; // 调用IBase. FWay
((ILeft)d). FWay (1) ; // 调用ILeft. FWay
((IRight)d). FWay (1) ; // 调用IBase. FWay
}
}

上例中,方法IBase.FWay在派生的接口ILeft中被Ileft的成员方法FWay覆盖了。所以对d.
FWay (1)的调用实际上调用了。即使从IBase-> IRight->
IDerived这条继承路径上来看,ILeft.FWay方法是没有被遮盖的。我们只要记住这或多或少:一旦成员被覆盖未来,所有对其的拜会都被遮住未来的
成员”拦截”了。

类对接口的兑现

前边大家已经说过,接口定义不包括方法的贯彻部分。接口能够透过类或结构来兑现。大家任重而道远描述通过类来贯彻接口。用类来贯彻接口时,接口的名目必须带有在类定义中的基类列表中。

下面的事例给出了由类来促成接口的例子。其中ISequence
为一个连串接口,提供了向队列尾部添加对象的成员方法Add( ),IRing
为一个循环表接口,提供了向环中插入对象的措施Insert(object
obj),方法重返插入的职位。类RingSquence 实现了接口ISequence
和接口IRing。

using System ;
interface ISequence {
object Add( ) ;
}
interface ISequence {
object Add( ) ;
}
interface IRing {
int Insert(object obj) ;
}
class RingSequence: ISequence, IRing
{
public object Add( ) {…}
public int Insert(object obj) {…}
}

尽管类实现了某个接口,类也隐式地持续了该接口的具备父接口,不管这多少个父接口有没有在类定义的基类表中列出。看上边的例证:

using System ;
interface IControl {
void Paint( );
}
interface ITextBox: IControl {
void SetText(string text);
}
interface IListBox: IControl {
void SetItems(string[] items);
}
interface IComboBox: ITextBox, IListBox { }

此处,
接口IcomboBox继承了ItextBox和IlistBox。类TextBox不仅实现了接口ITextBox,还落实了接口ITextBox
的父接口IControl。

前方我们曾经看到,一个类可以实现四个接口。再看上面的事例:

interface IDataBound {
void Bind(Binder b);
}
public class EditBox: Control, IControl, IDataBound {
public void Paint( );
public void Bind(Binder b) {…}
}

类EditBox从类Control中派生并且实现了Icontrol和IdataBound。在前方的例子中接口Icontrol中的Paint方
法和IdataBound接口中的Bind方法都用类EditBox中的公共成员贯彻。C#提供一种实现那个点子的可选择的路径,这样可以使执行这些的类
防止把这些分子设定为国有的。接口成员可以用有效的称谓来落实。例如,类EditBox能够改作方法Icontrol.Paint和
IdataBound.Bind来来实现。

public class EditBox: IControl, IDataBound {
void IControl.Paint( ) {…}
void IDataBound.Bind(Binder b) {…}
}

因为通过外部指派接口成员贯彻了每个成员,所以用这种情势实现的成员称为外部接口成员。外部接口成员可以只是经过接口来调用。例如,Paint方法中EditBox的贯彻可以只是透过成立Icontrol接口来调用。

class Test {
static void Main( ) {
EditBox editbox = new EditBox( );
editbox.Paint( ); //错误: EditBox 没有Paint 事件
IControl control = editbox;
control.Paint( ); // 调用 EditBox的Paint事件
}
}

上例中,类EditBox 从Control 类继承并还要落实了IControl and IDataBound
接口。EditBox 中的Paint 方法来自IControl 接口,Bind 方法来自IDataBound
接口,二者在EditBox 类中都作为国有成员贯彻。当然,在C#
中大家也得以接纳不作为公有成员贯彻接口。

只要每个成员都强烈地提议了被实现的接口,通过这种路径被实现的接口我们誉为显式接口成员(explicit
interface member)。 用这种方法我们改写上边的事例:

public class EditBox: IControl, IDataBound {
void IControl.Paint( ) {…}
void IDataBound.Bind(Binder b) {…}
}

显式接口成员只好通过接口调用。例如:

class CTest {
static void Main( ) {
EditBox editbox = new EditBox( ) ;
editbox.Paint( ) ; //错误:不同的主意
IControl control = editbox;
control.Paint( ) ; //调用 EditBox的Paint方法
}
}

上述代码中对editbox.Paint( )的调用是张冠李戴的,因为editbox
本身并没有提供这一方法。control.Paint( )是不易的调用模式。

讲明:接口本身不提供所定义的分子的落实,它仅仅表达这几个分子,这么些成员必须依靠实现接口的类或其余接口的支撑。

知道了什么访问接口,我们还要领会怎么着实现接口,要兑现C#的接口,请看下一节-实现接口

类对接口的实现

面前我们已经说过,接口定义不包括方法的兑现部分。接口可以经过类或结构来实现。大家重要描述通过类来兑现接口。用类来兑现接口时,接口的称呼必须带有在类定义中的基类列表中。

下边的例子给出了由类来促成接口的例证。其中ISequence
为一个行列接口,提供了向队列尾部添加对象的分子方法Add( ),IRing
为一个循环表接口,提供了向环中插入对象的艺术Insert(object
obj),方法重临插入的职位。类RingSquence 实现了接口ISequence
和接口IRing。

using System ;
interface ISequence {
object Add( ) ;
}
interface ISequence {
object Add( ) ;
}
interface IRing {
int Insert(object obj) ;
}
class RingSequence: ISequence, IRing
{
public object Add( ) {…}
public int Insert(object obj) {…}
}

只要类实现了某个接口,类也隐式地持续了该接口的装有父接口,不管那么些父接口有没有在类定义的基类表中列出。看下面的事例:

using System ;
interface IControl {
void Paint( );
}
interface ITextBox: IControl {
void SetText(string text);
}
interface IListBox: IControl {
void SetItems(string[] items);
}
interface IComboBox: ITextBox, IListBox { }

此处,
接口IcomboBox继承了ItextBox和IlistBox。类TextBox不仅实现了接口ITextBox,还实现了接口ITextBox
的父接口IControl。

眼前我们早就见到,一个类可以实现多少个接口。再看下面的事例:

interface IDataBound {
void Bind(Binder b);
}
public class EditBox: Control, IControl, IDataBound {
public void Paint( );
public void Bind(Binder b) {…}
}

类EditBox从类Control中派生并且实现了Icontrol和IdataBound。在面前的事例中接口Icontrol中的Paint方
法和IdataBound接口中的Bind方法都用类EditBox中的公共成员贯彻。C#提供一种实现这多少个艺术的可挑选的路子,这样可以使实践这些的类
避免把那个成员设定为公共的。接口成员可以用卓有功用的称号来落实。例如,类EditBox可以改作方法Icontrol.Paint和
IdataBound.Bind来来实现。

public class EditBox: IControl, IDataBound {
void IControl.Paint( ) {…}
void IDataBound.Bind(Binder b) {…}
}

因为经过外部指派接口成员贯彻了各样成员,所以用这种办法实现的积极分子称为外部接口成员。外部接口成员可以只是因此接口来调用。例如,Paint方法中EditBox的实现可以只是经过创造Icontrol接口来调用。

class Test {
static void Main( ) {
EditBox editbox = new EditBox( );
editbox.Paint( ); //错误: EditBox 没有Paint 事件
IControl control = editbox;
control.Paint( ); // 调用 EditBox的Paint事件
}
}

上例中,类EditBox 从Control 类继承并同时落实了IControl and IDataBound
接口。EditBox 中的Paint 方法来自IControl 接口,Bind 方法来自IDataBound
接口,二者在EditBox 类中都当做国有成员贯彻。当然,在C#
中大家也可以采用不作为公有成员贯彻接口。

即便每个成员都彰着地提出了被实现的接口,通过这种路径被实现的接口我们誉为显式接口成员(explicit
interface member)。 用这种办法我们改写上边的例子:

public class EditBox: IControl, IDataBound {
void IControl.Paint( ) {…}
void IDataBound.Bind(Binder b) {…}
}

显式接口成员只好通过接口调用。例如:

class CTest {
static void Main( ) {
EditBox editbox = new EditBox( ) ;
editbox.Paint( ) ; //错误:不同的方法
IControl control = editbox;
control.Paint( ) ; //调用 EditBox的Paint方法
}
}

上述代码中对editbox.Paint( )的调用是荒谬的,因为editbox
本身并不曾提供这一格局。control.Paint( )是科学的调用模式。

诠释:接口本身不提供所定义的成员的贯彻,它独自表明那多少个成员,这么些分子必须借助实现接口的类或其他接口的协理。
 

 第一节 接口慨述

接口(interface)用来定义一种程序的签订。实现接口的类如故社团要与接口的定义严俊平等。
有了这一个协定,就足以摒弃编程语言的界定(理论
上)。接口可以从三个基接口继承,
而类或协会可以实现多少个接口。接口可以蕴涵方法、属性、事件和索引器。
接口本身不提供它所定义的分子的落实。接口只指定
实现该接口的类或接口必须提供的成员。

接口比作一种模版,这种模版定义了对象必须兑现的主意,其目标就是让那几个办法可以用作接口实例被引述。
接口不能被实例化。类可以兑现三个接口并且通过那一个实现的接口被索引。
接口变量只可以索引实现该接口的类的实例。例子:

interface IMyExample {
string this[int index] { get ; set ; }
event EventHandler Even ;
void Find(int value) ;
string Point { get ; set ; }
}
public delegate void EventHandler(object sender, Event e) ;

地方例子中的接口包含一个索引this、一个事变伊芙n、一个办法Find和一个特性Point。

接口可以辅助多重继承。就像在下例中,接口”IComboBox”同时从”ITextBox”和”IListBox”继承。

interface IControl {
void Paint( ) ;
}
interface ITextBox: IControl {
void SetText(string text) ;
}
interface IListBox: IControl {
void SetItems(string[] items) ;
}
interface IComboBox: ITextBox, IListBox { }

类和布局可以多重实例化接口。就像在下例中,类”EditBox”继承了类”Control”,
同时从”IDataBound”和”IControl”继承。

interface IDataBound {
void Bind(Binder b) ;
}
public class EditBox: Control, IControl, IDataBound {
public void Paint( ) ;
public void Bind(Binder b) {…}
}

在下边的代码中,”Paint”方法从”IControl”接口而来;”Bind”方法从”IDataBound”接口而来,
皆以”public”的地位在”EditBox”类中落实。

说明:

1、C#中的接口是独自于类来定义的。这与 C++模型是周旋的,在
C++中接口实际上就是抽象基类。

2、接口和类都可以持续五个接口。

3、而类可以继承一个基类,接口根本不能继承类。那种模型制止了
C++的多继承问题,
C++中不同基类中的实现可能出现争论。由此也不再需要诸如虚拟继承和显式功能域这类复杂机制。
C#的简化接口模型有助于加速应用程序的支出。

4、一个接口定义一个唯有空虚成员的引用类型。C#中一个接口实际所做的,仅仅只存在着法子标明,
但一向就从未履行代码。这就暗示了不可能实例化一个接口,只可以实例化一个派生自该接口的靶子。

5、接口可以定义方法、属性和目录。所以,相比一个类,接口的特殊性是:当定义一个类时,可以派生自多重接口,
而你只好能够从仅有的一个类派生。

接口与组件

接口描述了组件对外提供的劳务。在组件和零部件之间、组件和客户之间都通过接口举行交互。
由此组件一旦宣布,它不得不通过先行定义的接口来提供客观的、一 致的劳务。
这种接口定义之间的平静使客户利用开发者可以协会出稳步的施用。
一个组件可以实现三个零部件接口,而一个一定的机件接口也可以被三个零部件来实
现。

组件接口必须是力所能及自己描述的。这表示组件接口应该不看重于具体的兑现,
将促成和接口分离彻底消除了接口的使用者和接口的实现者之间的耦合关系,增强了消息的包装程度。
与此同时这也要求组件接口必须使用一种与组件实现无关的语言。近期组件接口的讲述标准是IDL语言。

鉴于接口是组件之间的商事,因而组件的接口一旦被公布,组件生产者就应当尽可能地维持接口不变,
其它对接口语法或语义上的改观,都有可能引致现有组件与客户之间的关联遭到损坏。

各样组件都是独立的,有其独特的效用,只可以通过接口与外面通信。当一个零部件需要提供新的劳动时,
可以通过扩展新的接口来实现。不会影响原接口已存在的客户。而新的客户可以再一次采用新的接口来拿到劳动。

组件化程序设计

组件化程序设计形式继承并提升了面向对象的主次设计方法。它把对象技术利用于系统规划,
对面向对象的顺序设计的贯彻过程作了更进一步的肤浅。我们得以把组件化程序设计形式用作构造系统的系统布局层次的点子,
同时能够利用面向对象的法门很方便地促成组件。

组件化程序设计强调真正的软件可重用性和可观的互操作性。它侧重于组件的发生和装配,这两地点共同构成了组件化程序设计的主题。
零件的暴发过程不仅仅
是采取序列的需求,组件市场本身也有助于了组件的提高,促进了软件厂商的交换与合作。
组件的装配使得软件出品可以利用类似于搭积木的情势急速地确立起来,不
仅可以缩小软件出品的开发周期,
再就是也进步了系统的安定团结和可靠性。

零件程序设计的章程有以下几个地方的特性:

1、编程语言和付出条件的独立性;

2、组件地点的透明性;

3、组件的进程透明性;

4、可扩充性;

5、可重用性;

6、具有强大的底子设备;

7、系统顶尖的公共服务;

C#语言由于其过多亮点,相当适用于组件编程。但这并不是说C#是一门组件编程语言,

也不是说C#提供了组件编程的工具。大家曾经三番五回提议,组件应该
具有与编程语言无关的特性。

请读者切记这或多或少:组件模型是一种标准,不管选取何种程序语言设计组件,都不可能不服从这一正规。

譬如说组装电脑的事例,只要逐项
厂商为我们提供的配件规格、接口符合统一的规范,这个配件组合起来就能协同工作,

零件编程也是均等。大家只是说,利用C#言语进行零部件编程将会给我们带来
更大的惠及。

精通了什么样是接口,接下去就是咋样定义接口,请看下一节–定义接口。

其次节 定义接口

从技术上讲,接口是一组包含了函数型方法的数据结构。通过这组数据结构,客户代码可以调用组件对象的效率。

概念接口的貌似格局为:
[attributes] [modifiers] interface identifier [:base-list]
{interface-body}[;]

说明:

1、attributes(可选):附加的定义性消息。

2、modifiers(可选): 允许拔取的修饰符有 new
和两个访问修饰符。分别是:new、public、protected、internal、 private。

在一个接口定义中一致修饰符不允许出现多次,new
修饰符只可以出现在嵌套接口中,表示覆盖了延续而来的同名成员。

The public, protected, internal, and private
修饰符定义了对接口的造访权限。

3、提醒器和事件。

4、identifier:接口名称。

5、base-list(可选):包含一个或多个显式基接口的列表,接口间由逗号分隔。

6、interface-body:对接口成员的概念。

7、接口可以是命名空间或类的分子,并且可以分包下列成员的签名:
方法、属性、索引器 。

8、一个接口可从一个或五个基接口继承。 

接口这多少个定义在C#和Java中极度相似。接口的基本点词是interface,一个接口可以增加一个要么五个其他接口。

遵从规矩,接口的名字以大写字母”I”起先。下边的代码是C#接口的一个例证,它与Java中的接口完全一致:

 

interface IShape
{
void Draw ( ) ;
}

如若您从多少个或者三个以上的接口派生,父接口的名字列表用逗号分隔,如下边的代码所示:

 

interface INewInterface: IParent1, IParent2
 {
 }

然而,与Java不同,C#中的接口无法包含域(菲尔德(Field))。其余还要注意,在C#中,接口内的具备办法默认都是公用方法。

在Java中,方法定义
可以蕴涵public修饰符(即便这毫不必要),但在C#中,显式为接口的点子指定public修饰符是非法的。

例如,下面的C#接口将爆发一个编译错 误。

 

interface IShape { public void Draw( ) ; }

上边的例证定义了一个名为IControl 的接口,接口中含有一个分子方法Paint:

 

interface IControl
 {
void Paint( ) ;
}

在下例中,接口 IInterface从五个基接口 IBase1 和 IBase2 持续:

 

interface IInterface: IBase1, IBase2
{
void Method1( ) ;
void Method2( ) ;
}

接口可由类实现。实现的接口的标识符出现在类的基列表中。例如:

 

class Class1: Iface1, Iface2
 {
// class 成员。
}

类的基列表同时含有基类和接口时,列表中第一出现的是基类。例如:

 

class ClassA: BaseClass, Iface1, Iface2
 {
// class成员。
}

以下的代码段定义接口IFace,它只有一个主意:

 

interface IFace
 {
void ShowMyFace( ) ;
}

不可能从这多少个概念实例化一个目的,但足以从它派生一个类。由此,该类必须实现ShowMyFace抽象方法:

 

class CFace:IFace
{
public void ShowMyFace( )
 {
Console.WriteLine(” implementation ” ) ;
}
}

基接口

一个接口可以从零或两个接口继承,这个被叫作那一个接口的显式基接口。

当一个接口有比零多的显式基接口时,那么在接口的概念中的形式为,

接口标识符后边跟着由一个冒号”:”和一个用逗号”,”分开的基接口标识符列表。

接口基:

:接口类型列表表明:

1、一个接口的显式基接口必须至少同接口本身一样可访问。例如,在一个国有接口的基接口中指定一个私家或内部的接口是谬误的。

2、一个接口直接或直接地从它自己连续是一无是处的。

3、接口的基接口都是显式基接口,并且是它们的基接口。

换句话说,基接口的成团完全由显式基接口和它们的显式基接口等等组成。在底下的例子中
interface IControl {
void Paint( ) ;
}
interface ITextBox: IControl {
void SetText(string text) ;
}
interface IListBox: IControl {
void SetItems(string[] items) ;
}
interface IComboBox: ITextBox, IListBox { }

IComboBox 的基接口是IControl, ITextBox, 和 IlistBox。

4、一个接口继承它的基接口的装有成员。换句话说,下边的接口 IComboBox
就像Paint一样持续成员SetText 和 SetItems。

5、一个兑现了接口的类或结构也隐含地实现了拥有接口的基接口。

接口主体

一个接口的接口主体定义接口的分子。

 

interface-body:
{ interface-member-declarationsopt }

概念接口重倘诺概念接口成员,请看下一节–定义接口成员。

其三节 定义接口成员

接口可以蕴涵一个和六个分子,那多少个分子可以是艺术、属性、索引指示器和事件,但无法是常量、域、操作符、

构造函数或析构函数,而且不能够包含其他静态成员。接口定义创设新的概念空间,并且接口定义直

 接包含的接口成员定义将新成员引入该定义空间。

说明:

1、接口的成员是从基接口继承的分子和由接口本身定义的分子。

2、接口定义能够定义零个或多少个分子。接口的积极分子必须是格局、属性、事件或索引器。

接口无法包含常数、字段、运算符、实例构造函数、析构函数或项目,也不可以包含其他类型的静态成员。

3、定义一个接口,该接口对于每种可能连串的分子都含有一个:方法、属性、事件和索引器。

4、接口成员默认访问形式是public。接口成员定义不可能包含其他修饰符,

譬如成员定义前无法加abstract,public,protected,internal,private,virtual,override
或static 修饰符。

5、接口的成员之间不可能互相同名。继承而来的积极分子不用再定义,

但接口可以定义与后续而来的成员同名的成员,这时我们说接口成员覆盖了连续而来的积极分子,
这不会促成错误,

但编译器会付出一个警戒。关闭警告提醒的艺术是在成员定义前增长一个new关键字。

但尽管没有遮盖父接口中的成员,使用new 关键字会导致编译器发出警示。

6、方法的名号必须与同等接口中定义的具有属性和事件的称号不同。此外,方法的签署必须与同一接口中定义的富有其他格局的签字不同。

7、属性或事件的称号必须与同一接口中定义的装有其他成员的称呼不同。

8、一个索引器的署名必须区分于在同一接口中定义的别样兼具索引器的签约。

9、接口方法表明中的属性(attributes), 重回类型(return-type),
标识符(identifier),

和情势参数列表(formal-parameter-lis)与一个类的主意申明中的那多少个有平等的意义。

一个接口方法表明不允许指定一个措施主体,而讲明 平常用一个支行截止。

10、接口属性阐明的拜访符与类属性注明的拜会符相对应,除了访问符主体通常必须用分号。

据此,无论属性是读写、只读或只写,访问符都完全确定。

11、接口索引注脚中的属性(attributes), 类型(type), 和样式参数列表
(formal-parameter-list)

与类的目录注明的那多少个有一样的含义。

下边例子中接口IMyTest包含了目录提示器、事件E、 方法F、 属性P 这一个分子:

interface IMyTest{
string this[int index] { get; set; }
event EventHandler E ;
void F(int value) ;
string P { get; set; }
}
public delegate void EventHandler(object sender, EventArgs e) ;

上边例子中接口IStringList包含每个可能类型成员的接口:一个措施,一个性质,一个事件和一个目录。

public delegate void StringListEvent(IStringList sender);
public interface IStringList
{
void Add(string s);
int Count { get; }
event StringListEvent Changed;
string this[int index] { get; set; }
}

接口成员的全权名

使用接口成员也可使用全权名(fully qualified
name)。接口的全权名称是如此组合的。

接口名加小圆点”.” 再跟成员名比如对于下面五个接口:

interface IControl {
void Paint( ) ;
}
interface ITextBox: IControl {
void GetText(string text) ;
}

里面Paint 的全权名是IControl.Paint,GetText的全权名是ITextBox.
GetText。当然,全权名中的成员名称必须是在接口中已经定义过的,比如采用ITextBox.Paint.就是不创设的。

设若接口是名字空间的分子,全权名还非得含有名字空间的称谓。

namespace System
{
public interface IDataTable {
object Clone( ) ;
}
}

这就是说Clone方法的全权名是System. IDataTable.Clone。

概念好了接口,接下去就是什么访问接口,请看下一节–访问接口 

 

一旦你设计一个和人互换的程序。
先成立一个接口 interface 人 //定义接口,它代表一个人,

{void Hello(); }//接口虚函数,用来跟这个人谈话
但不同的人有永不的交流情势,具体方法用类来贯彻,比如。

 class 美利坚同盟国人:人 //继承接口“人” 然后,类里实例化接口函数

void Hello(){说hi;}

class 中国人:人 //继承接口“人” 然后,类里实例化接口函数 void
Hello(){说你好;}

 class SB:人 //sb也是人 实现 Hello{说xxxxx;}
最终你的程序运行时,就用接口“人”就可以了,

因为无论是遭遇何人(United States人,中国人,仍旧sb),都得以和她们互换了,这就是接口的含义!!!
 

1、显式实现接口成员

为了贯彻接口,类可以定义显式接口成员执行体(Explicit interface member
implementations)。

显式接口成员执行体可以是一个艺术、一个性质、一个事件仍然是一个索引指示器的概念,定义与该成员对应的全权名应保持一致。

using System ;
interface ICloneable
{
object Clone( ) ;
}

interface IComparable
{
int CompareTo(object other) ;
}
class ListEntry: ICloneable, IComparable
{
object ICloneable.Clone( )
 {…}

int IComparable.CompareTo(object other)
{…}

}

下面的代码中ICloneable.Clone 和IComparable.CompareTo
就是显式接口成员执行体。

说明:

1、无法在点子调用、属性访问以及索引提示器访问中经过全权名访问显式接口成员执行体。

骨子里,显式接口成员执行体只好通过接口的实例,仅仅引用接口的积极分子名称来拜会。

2、显式接口成员执行体不可能应用任何访问限制符,也不可以增长abstract,
virtual, override或static 修饰符。

3、显式接口成员执行体和其他成员具有不同的拜访情势。因为无法在章程调用、

性能访问以及索引指示器访问中经过全权名访问,显式接口成员执行体在某种意义上是私有的。

但它们又可以通过接口的实例访问,也富有一定的公有性质。

4、只有类在概念时,把接口名写在了基类列表中,而且类中定义的全权名、类型和重临类型都与显式接口成员执行体完全一致时,
显式接口成员执行体才是有效的,例如:

class Shape: ICloneable
{
object ICloneable.Clone( )
{…}

int IComparable.CompareTo(object other)
 {…}
}

采纳显式接口成员执行体经常有多个目标:

1、因为显式接口成员执行体无法因此类的实例举办访问,这就可以从国有接口中把接口的兑现部分单独分离开。

一旦一个类只在其中拔取该接口,而类的使用者不会直接行使到该接口,这种显式接口成员执行体就足以起到效益。

2、显式接口成员执行体避免了接口成员之间因为同名而爆发混淆。
假如一个类希望对名称和重返类型相同的接口成员运用不同的落实情势,
这就必须要使用到显式接口成员执行体。倘诺没有显式接口成员执行体,那么对于名称和重临类型不同的接口成员,
类也不知所措开展落实。

下面的概念是无效的,因为Shape 定义时基类列表中没有出现接口IComparable。

class Shape: ICloneable
{
object ICloneable.Clone( ) {…}
}
class Ellipse: Shape
{
object ICloneable.Clone( ) {…}
}

在Ellipse
中定义ICloneable.Clone是荒唐的,因为Ellipse即便隐式地贯彻了接口ICloneable,
ICloneable仍旧没有显式地现身在Ellipse定义的基类列表中。

接口成员的全权名必须对应在接口中定义的成员。如上面的例子中,Paint的显式接口成员执行体必须写成IControl.Paint。

using System ;
interface IControl
{
void Paint( ) ;
}
interface ITextBox: IControl
{
void SetText(string text) ;
}
class TextBox: ITextBox
{
void IControl.Paint( ) {…}
void ITextBox.SetText(string text) {…}
}

 

心想事成接口的类可以显式实现该接口的成员。当显式实现某成员时,不能透过类实例访问该成员,

而不得不通过该接口的实例访问该成员。显式接口实现还同意程序员继承共享相同成员名的多少个接口,并为每个接口成员提供一个独立的兑现。

下边例子中并且以公制单位和英制单位彰显框的尺寸。Box类继承
IEnglishDimensions和 IMetricDimensions多个接口,

它们表示不同的度量衡系统。几个接口有雷同的分子名 Length 和 Width。

程序清单1 DemonInterface.cs

interface IEnglishDimensions {
float Length ( ) ;
float Width ( ) ;
}
interface IMetricDimensions {
float Length ( ) ;
float Width ( ) ;
}
class Box : IEnglishDimensions, IMetricDimensions {
float lengthInches ;
float widthInches ;
public Box(float length, float width) {
lengthInches = length ;
widthInches = width ;
}
float IEnglishDimensions.Length( ) {
return lengthInches ;
}
float IEnglishDimensions.Width( ) {
return widthInches ;
}
float IMetricDimensions.Length( ) {
return lengthInches * 2.54f ;
}
float IMetricDimensions.Width( ) {
return widthInches * 2.54f ;
}
public static void Main( ) {
//定义一个实类对象 “myBox”::
Box myBox = new Box(30.0f, 20.0f);
// 定义一个接口” eDimensions”::
IEnglishDimensions eDimensions = (IEnglishDimensions) myBox;
IMetricDimensions mDimensions = (IMetricDimensions) myBox;
// 输出:
System.Console.WriteLine(” Length(in): {0}”, eDimensions.Length( ));
System.Console.WriteLine(” Width (in): {0}”, eDimensions.Width( ));
System.Console.WriteLine(” Length(cm): {0}”, mDimensions.Length( ));
System.Console.WriteLine(” Width (cm): {0}”, mDimensions.Width( ));
}
}

输出:Length(in): 30,Width (in): 20,Length(cm): 76.2,Width (cm): 50.8

代码商量:如果愿意默认度量接纳英制单位,请正常实现 Length 和 Width
这六个艺术,

并从 IMetricDimensions 接口显式实现 Length 和 Width 方法:

public float Length( ) {
return lengthInches ;
}
public float Width( ){
return widthInches;
}
float IMetricDimensions.Length( ) {
return lengthInches * 2.54f ;
}
float IMetricDimensions.Width( ) {
return widthInches * 2.54f ;
}

这种气象下,能够从类实例访问英制单位,而从接口实例访问公制单位:

System.Console.WriteLine(“Length(in): {0}”, myBox.Length( )) ;
System.Console.WriteLine(“Width (in): {0}”, myBox.Width( )) ;
System.Console.WriteLine(“Length(cm): {0}”, mDimensions.Length( )) ;
System.Console.WriteLine(“Width (cm): {0}”, mDimensions.Width( )) ;

2、继承接口实现

接口具有不变性,但这并不意味着接口不再提升。类似于类的继承性,
接口也得以延续和进化。

注意:接口继承和类继承不同,首先,类继承不仅是验证继承,而且也是兑现连续;
而接口继承只是表明继承。也就是说,派生类可以继续基类的措施实现,

派生的接口只持续了父接口的成员方法求证,而尚未继续父接口的贯彻,其次
,C#中类继承只允许单继承,然则接口继承允许多继承,一个子接口可以有两个父接
口。

接口可以从零或六个接口中继续。从四个接口中连续时,用”:”后跟被延续的接口名字,
三个接口名之间用”,”分割。被连续的接口应该是足以访问得到 的,
譬如说从private 类型或internal
类型的接口中继承就是不允许的。接口不容许直接或直接地从自家继承。
和类的接续相似,接口的继承也形成接口之间的层次结构。

请看下面的事例:

using System ;
interface IControl {
void Paint( ) ;
}
interface ITextBox: IControl {
void SetText(string text) ;
}
interface IListBox: IControl {
void SetItems(string[] items) ;
}
interface IComboBox: ITextBox, IListBox { }

对一个接口的存续也就延续了接口的有着成员,下边的事例中接口ITextBox和IListBox都从接口IControl中继承,

也就连续了接口
IControl的Paint方法。接口IComboBox从接口ITextBox和IListBox中延续,由此它应该继承了接口ITextBox的
SetText方法和IListBox的SetItems方法,还有IControl的Paint方法。
一个类继承了所有被它的基本类提供的接口实现程序。

不通过显式的贯彻一个接口,一个派生类不可以用任何模式改变它从它的基本类继承的接口映射。例如,在宣称中

interface IControl {
void Paint( );
}
class Control: IControl {
public void Paint( ) {…}
}
class TextBox: Control {
new public void Paint( ) {…}
}

TextBox 中的方法Paint 隐藏了Control中的方法Paint
,不过没有变动从Control.Paint 到IControl.Paint 的照耀,
而透过类实例和接口实例调用Paint将会有下边的熏陶

Control c = new Control( ) ;
TextBox t = new TextBox( ) ;
IControl ic = c ;
IControl it = t ;
c.Paint( ) ; // 影响Control.Paint( ) ;
t.Paint( ) ; // 影响TextBox.Paint( ) ;
ic.Paint( ) ; // 影响Control.Paint( ) ;
it.Paint( ) ; // 影响Control.Paint( ) ;

不过,当一个接口方法被映射到一个类中的虚拟方法,派生类就不容许覆盖这多少个编造方法并且改变接口的兑现函数。

诸如,把地方的宣示重新写为

interface IControl {
void Paint( ) ;
}
class Control: IControl {
public virtual void Paint( ) {…}
}
class TextBox: Control {
public override void Paint( ) {…}
}

就会看到下面的结果:

Control c = new Control( ) ;
TextBox t = new TextBox( ) ;
IControl ic = c ;
IControl it = t ;
c.Paint( ) ; // 影响Control.Paint( );
t.Paint( ) ; // 影响TextBox.Paint( );
ic.Paint( ) ; // 影响Control.Paint( );
it.Paint( ) ; // 影响TextBox.Paint( );

是因为显式接口成员贯彻程序不可能被声称为虚构的,就不能覆盖一个显式接口成员贯彻程序。

一个显式接口成员贯彻程序调用此外一个主意是有效的,而除此以外的非凡格局可以被声称为虚构的以便让派生类可以覆盖它。例如:

interface IControl {
void Paint( ) ;
}
class Control: IControl {
void IControl.Paint( ) { PaintControl( ); }
protected virtual void PaintControl( ) {…}
}
class TextBox: Control {
protected override void PaintControl( ) {…}
}

此间,从Control 继承的类可以通过覆盖措施PaintControl 来对IControl.Paint
的兑现程序开展特殊化。

3、重新实现接口

我们曾经介绍过,派生类可以对基类中已经定义的积极分子方法开展重载。类似的概念引入到类对接口的实现中来,

何谓接口的重落实(re-
implementation)。继承了接口实现的类可以对接口举办重落实。

以此接口要求是在类定义的基类列表中冒出过的。对接口的重落实也必须严酷地听从首次实现接口的条条框框,

派生的接口映射不会对为接口的重落实所创立的接口映射暴发其他影响。

下面的代码给出了接口重落实的例证:

interface IControl {
void Paint( ) ;
class Control: IControl
void IControl.Paint( ) {…}
class MyControl: Control, IControl
public void Paint( ) {}
}

实在就是:Control把IControl.Paint映射到了Control.IControl.Paint上,但这并不影响在
MyControl中的重落实。

在MyControl中的重落实中,IControl.Paint被映射到MyControl.Paint 之上。

在接口的重落实时,继承而来的公有成员定义和继续而来的显式接口成员的定义参预到接口映射的长河。

using System ;
interface IMethods {
void F( ) ;
void G( ) ;
void H( ) ;
void I( ) ;
}
class Base: IMethods {
void IMethods.F( ) { }
void IMethods.G( ) { }
public void H( ) { }
public void I( ) { }
}
class Derived: Base, IMethods {
public void F( ) { }
void IMethods.H( ) { }
}

这边,接口IMethods在Derived中的实现把接口方法映射到了Derived.F,Base.IMethods.G,
Derived.IMethods.H,
 还有Base.I。前面大家说过,类在贯彻一个接口时,同时隐式地落实了该接口的具有父接口。
一律,类在重落实一个接口时还要,隐式地重落实了该接口的所 有父接口。

using System ;
interface IBase {
void F( ) ;
}
interface IDerived: IBase {
void G( ) ;
}
class C: IDerived {
void IBase.F( ) {
//对F 举行落实的代码…
}
void IDerived.G( ) {
//对G 举办落实的代码…
}
}
class D: C, IDerived {
public void F( ) {
//对F 举办落实的代码…
}
public void G( ) {
//对G 举行落实的代码…
}
}

此间,对IDerived的重落实也同样实现了对IBase的重落实,把IBase.F
映射到了D.F。

4、映射接口

类必须为在基类表中列出的有所接口的成员提供具体的兑现。在类中一定接口成员的贯彻称之为接口映射(interface
mapping )。

辉映,数学上代表一一对应的函数关系。接口映射的意义也是一律,接口通过类来贯彻,那么对于在接口中定义的每一个分子,都应该相应着类的一个成员来为它提供具体的落实。

类的成员及其所映射的接口成员之间必须满足下列条件:

1、假使A和B都是成员方法,那么A和B的名号、类型、形参表(包括参数个数和每一个参数的连串)都应当是一律的。

2、假设A和B都是性质,那么A和B的名号、类型应当平等,而且A和B的访问器也是近乎的。但一旦A不是显式接口成员执行体,

A允许扩张和谐的访问器。

3、假设A和B都是光阴那么A和B的称号、类型应当平等。

4、假如A和B都是索引提醒器,那么A和B的花色、形参表(包括参数个数和每一个参数的档次)应当平等。而且A和B的访问器也是相近的。

但假使A不是显式接口成员执行体,A允许扩展自己的访问器。

这就是说,对于一个接口成员,如何确定由哪一个类的积极分子来促成啊?即一个接口成员映射的是哪一个类的成员?

在此间,我们讲述一下接口映射的长河。假若类C
实现了一个接口IInterface,Member是接口IInterface中的一个分子,在固定由谁来实现接口成员Member,即Member的映
射过程是这么的:

1、假设C中留存着一个显式接口成员执行体,该执行体与接口IInterface
及其成员Member相对应,则由它来贯彻Member 成员。

2、假使基准(1)不满意,且C中留存着一个非静态的公有成员,该成员与接口成员Member相对应,则由它来落实Member
成员。

3、假设上述标准仍不知足,则在类C定义的基类列表中寻觅一个C
的基类D,用D来代替C。

4、重复步骤1– 3
,遍历C的有所直接基类和非直接基类,直到找到一个知足条件的类的成员。

5、假如仍旧没有找到,则告诉错误。

下面是一个调用基类方法来落实接口成员的事例。类Class2
实现了接口Interface1,类Class2 的基类Class1 的分子也涉足了接口的炫耀,

也就是说类Class2
在对接口Interface1举办落实时,使用了类Class1提供的积极分子方法F来促成接口Interface1的成员方法F:

interface Interface1 {
void F( ) ;
}
class Class1 {
public void F( ) { }
public void G( ) { }
}
class Class2: Class1, Interface1 {
new public void G( ) {}
}

小心:接口的积极分子包括它和谐定义的成员,而且包括该接口所有父接口定义的分子。在接口映射时,

非但要对接口定义体中显式定义的享有成员开展映射,而且要对隐式地从父接口这里继承来的具备接口成员开展映射。

在进行接口映射时,还要小心下边两点:

1、在支配由类中的哪个成员来促成接口成员时,类中显式表明的接口成员比其他成员优先实现。

2、使用Private、protected和static修饰符的分子不可能加入实现接口映射。例如:

interface ICloneable {
object Clone( ) ;
}
class C: ICloneable {
object ICloneable.Clone( ) {…}
public object Clone( ) {…}
}

事例中成员ICloneable.Clone 称为接口ICloneable 的积极分子Clone
的实现者,因为它是显式表明的接口成员,

比其他成员具有更高的优先权。

设若一个类实现了五个或五个以上名字、类型和参数类型都相同的接口,那么类中的一个成员就可能实现所有这个接口成员:

interface IControl {
void Paint( ) ;
}
interface IForm {
void Paint( ) ;
}
class Page: IControl, IForm {
public void Paint( ) {…}
}

此间,接口IControl和IForm的艺术Paint都映射到了类Page中的Paint方法。
本来也足以分级用显式的接口成员分头实现这三个措施:

interface IControl {
void Paint( ) ;
}
interface IForm {
void Paint( ) ;
}
class Page: IControl, IForm {
public void IControl.Paint( ) {
//具体的接口实现代码
}
public void IForm.Paint( ) {
//具体的接口实现代码
}
}

下边的两种写法都是不利的。可是假诺接口成员在继承中覆盖了父接口的积极分子,

那么对该接口成员的贯彻就可能必须映射到显式接口成员执行体。看下边的事例:

interface IBase {
int P { get; }
}
interface IDerived: IBase {
new int P( ) ;
}

接口IDerived从接口IBase中延续,这时接口IDerived
的成员方法覆盖了父接口的分子方法。

因为这时存在着同名的多个接口成员,那么对这三个接口成员的实现假设不利用显式接口成员执行体,

编译器将不能辨识接口
映射。所以,假如某个类要贯彻接口IDerived,在类中务必至少定义一个显式接口成员执行体。

采取下边那些写法都是理所当然的:

//一:对六个接口成员都应用显式接口成员执行体来实现
lass C: IDerived {
int IBase.P
get
{ //具体的接口实现代码 }
int IDerived.P( ){
//具体的接口实现代码 }
}
//二:对Ibase 的接口成员利用显式接口成员执行体来实现
class C: IDerived {
int IBase.P
get {//具体的接口实现代码}
public int P( ){
//具体的接口实现代码 }
}
//三:对IDerived 的接口成员接纳显式接口成员执行体来实现
class C: IDerived{
public int P
get {//具体的接口实现代码}
int IDerived.P( ){
//具体的接口实现代码}
}

另一种情状是,即使一个类实现了两个接口,那一个接口又有着同一个父接口,这几个父接口只同意被实现一回。

using System ;
interface IControl {
void Paint( ) ;
interface ITextBox: IControl {
void SetText(string text) ;
}
interface IListBox: IControl {
void SetItems(string[] items) ;
}
class ComboBox: IControl, ITextBox, IListBox {
void IControl.Paint( ) {…}
void ITextBox.SetText(string text) {…}
void IListBox.SetItems(string[] items) {…}
}

上边的事例中,类ComboBox实现了五个接口:IControl,ITextBox和IListBox。
虽然认为ComboBox不仅实现了
IControl接口,而且在落实ITextBox和IListBox的同时,
又分别实现了它们的父接口IControl。实际上,对接口 ITextBox 和IListBox
的兑现,
分享了对接口IControl 的落实。

我们对C#的接口有了较完美的认识,基本领悟了哪些应用C#的接口编程,但实际,
C#的不光应用于.net平台,它同样协助从前的COM,可以实现COM类到.NET类的转移,如C#调用API。
欲了然这下面的学问,请看下一节-接口转换。
 

 
对此各位使用面向对象编程语言的程序员来说,“接口”这个名词一定不生疏,

唯独不知诸位有没有诸如此类的困惑:接口有哪些用场?它和抽象类有什么样区别?能不可以用抽象类代替接口呢?而且,

作为程序员,一定平常听到“面向接口编程”这么些短语,

那么它是何许意思?有怎样考虑内涵?和面向对象编程是什么关联?本文将顺序解答那么些疑点。
 
  1.面向接口编程和面向对象编程是咋样关系

  首先,面向接口编程和面向对象编程并不是同级的,
它并不是比面向对象编程更上进的一种独立的编程思想,而是附属于面向对象思想连串,
属于其部分。或者说,它是面向对象编程系列中的思想精华之一。

  2.接口的龙虎山真面目

  接口,在表面上是由多少个从未重点代码的措施定义组成的集合体,有唯一的称号,
可以被类或任何接口所实现(或者也能够说继续)。它在形式上可能是之类的规范:

  

以下是援引片段:
interface InterfaceName
  {
  void Method1();
  void Method2(int para1);
  void Method3(string para2,string para3);
  }
  那么,接口的精神是什么吧?或者说接口存在的意义是哪些。我认为可以从以下五个意见考虑:
  1)接口是一组规则的聚合,它规定了实现本接口的类或接口必须具有的一组规则。
反映了宇宙空间“假诺你是……则必须能……”的眼光。

  例如,在宇宙中,人都能吃饭,即“假如您是人,则必须能吃饭”。
那么模拟到电脑程序中,就相应有一个IPerson(习惯上,接口名由
“I”最先)接口,
并有一个措施叫Eat(),然后大家规定,每一个表示“人”的类,必须实现IPerson接口,
这就仿照了宇宙空间“倘诺您是人,则必须能吃饭”那条规则。

  从那边,我想各位也能收看有些面向对象思想的事物。面向对象思想的基本之一,就是仿照真实世界,
把真正世界中的事物抽象成类,整个程序靠各系列的实例相互通信、相互合作完成系统机能,
这分外适合实际世界的运行情形,也是面向对象思想的精华。

  2)接口是在大势所趋粒度视图上同类事物的肤浅意味。注意这里自己强调了在自然粒度视图上,
因为“同类事物”这么些定义是相对的,它因为粒度视图不同而各异。

  例如,在自身的眼里,我是一个人,和一头猪有本质区别,我得以承受自己和我同学是同类这一个说法,
但绝无法接受我和一头猪是同类。可是,假设在一个动物学家眼里,我和猪应该是同类,因为大家都是动物,
她可以认为“人”和“猪”都落实了IAnimal那一个接口,而她在探讨动物行为时,不会把我和猪分开对待,
而会从“动物”这一个较大的粒度上探究,但他会以为自身和一棵树有本质区别。

  现在换了一个遗传学家,意况又不同了,因为生物都能遗传,所以在她眼里,我非但和猪没分别,
和一只蚊子、一个细菌、一颗树、一个迁延乃至一个 SARS病毒都没关系区别,
因为她会觉得咱们都落实了IDescendable那么些接口(注:descend vi.
遗传),即大家都是可遗传的事物,
他不会独家研商大家,而会将具有生物作为同类举行研讨,在她眼里没有人和病毒之分,
唯有可遗传的物质和不可遗传的物质。但至少,我和一块石头依然有分此外。

  可不幸的政工时有爆发了,某日,地球下边世了一位伟大的人,他叫列宁,他在熟读马克思(Marx)、
恩格斯(格斯)的辩证唯物主义思想巨著后,颇有体验,于是她下了一个知名的概念:所谓物质,
不畏能被发觉所反映的客观实在。至此,我和一块石头、一丝空气、
一条成语和传导手机信号的电磁场已经没关系区别了,因为在列宁的眼里,
俺们都是足以被察觉所反映的客观实在。倘使列宁是一名程序员,他会如此说:
所谓物质,就是享有同时落实了“IReflectabe”和“IEsse”
五个接口的类所生成的实例。
(注:reflect v. 反映 esse n. 客观实在)

  也许你会以为自家上边的例子像在瞎掰,不过,这多亏接口得以存在的意义。
面向对象思想和主导之一名叫多态性,

哪些叫多态性?说白了就是在某个粒度视图层面上对同类事物不加区其余对待而统一处理。

而之所以敢如此做,就是因为有接口的存在。像非凡遗传学家,他领略所有生物都落实了
IDescendable接口,

这如果是生物,一定有Descend()那一个法子,于是他就足以统一研究,而不致于分别研商每一种生物而最终累死。

  可能这边还不可能给你一个关于接口本质和机能的直观印象。那么在后文的事例和对几个设计格局的分析中,
你将会更直观感受到接口的内涵。

  3.面向接口编程综述

  通过上文,我想我们对接口和接口的想想内涵有了一个询问,那么咋样是面向接口编程呢?我个人的定义是:

在系统分析和架构中,分清层次和倚重性关系,每个层次不是一贯向其上层提供劳动(即不是一向实例化在上层中),

而是通过定义一组接口,仅向上层透露其接口效能,上层对于下层仅仅是接口倚重,而不看重具体类。

  这样做的裨益是通晓的,首先对系统灵活性大有补益。当下层需要变更时,只要接口及接口功效不变
,则上层不用做任何改动。甚至足以在不转移上层代码时将下层整个替换掉,
就像我们将一个WD的60G硬盘换成一个西部数据的160G的硬盘,总括机其他地点不用做任何改动,而是把原硬盘拔下来、
新硬盘插上就行了,因为总计机其他一些不借助于具体硬盘,而只依靠一个IDE接口,
假诺硬盘实现了这么些接口,就足以替换上去。从此处看,程序中的接口和切实中的接口极为一般,
于是自己直接觉得,接口(interface)这么些词用的正是神似!

  使用接口的另一个益处就是不同部件或层次的开发职员可以并行开工,就像造硬盘的永不等造CPU的,
也不用等造突显器的,只要接口一致,设计合理,完全可以互相举行开发,从而提升功效。

  本篇小说先到此处。最终自己想再啰嗦一句:面向对象的精髓是效仿现实,那也得以说是本人这篇作品的神魄。
故此,多从现实中思索面向对象的东西,对增强系统分析规划能力大有脾益。

  下篇作品,我将用一个实例来映现接口编程的主导措施。

  而第三篇,我将分析经典设计情势中的一些面向接口编程思想,并分析一下.NET分层架构中的面向接口思想。

  对本文的补偿:

  1.关于“面向接口编程”中的“接口”与具象面向对象语言中“接口”五个词

  看到有朋友指出“面向接口编程”中的“接口”二字应该比单纯编程语言中的interface范围更大。
本身通过思考,觉得很有道理。那里自己写的真的不太合理。我想,面向对象语言中的“接口”是指现实的一种代码结构
,例如C#中用interface关键字定义的接口。而“面向接口编程”中的“接口”
可以说是一种从软件架构的角度、
从一个更抽象的局面上指这种用于隐藏具体底层类和贯彻多态性的布局部件。从这么些含义上说,
假如定义一个抽象类,并且目标是为着实现多态,那么我觉着把这些抽象类也叫做“接口”是在理的。
不过用抽象类实现多态合理不创制?在底下第二条商量。

  概括来说,我觉着六个“接口”的定义既相互区分又相互联系。
“面向接口编程”中的接口是一种沉思层面的用于落实多态性、提升软件灵活性和可维护性的架构部件,
而具体语言中的“接口”是将这种思维中的部件具体实施到代码里的一手。

  2.有关抽象类与接口

  看到回复中那是探讨的相比猛烈的一个题材。很对不起我考虑不周没有在著作中研讨那么些问题。
我个人对那么些题目标接头如下:

  尽管单从切实代码来看,对这多少个概念很容易混淆视听,甚至认为接口就是剩下的,
因为单从切实功能来看,除多重继承外(C#,Java中),抽象类似乎完全能代表接口。
不过,难道接口的存在是为着贯彻多重继承?当然不是。我以为,抽象类和接口的区别在于选用动机。
运用抽象类是为着代码的复用,而利用接口的胸臆是为着落实多态性。所以,
尽管您在为某个地点该利用接口如故抽象类而犹豫不决不决时,那么可以考虑你的动机是什么。

  看到有对象对IPerson这一个接口的质询,我个人的知晓是,IPerson这一个接口该不该定义,
重要看现实应用中是怎么个意况。假诺我们的门类中有Women和Man,都卫冕Person,
还要Women和Man绝大多数办法都平等,唯有一个主意DoSomethingInWC()不同(例子对比粗俗,各位见谅)
,那么自然定义一个AbstractPerson抽象类相比客观,因为它可以把其它具有办法都包含进去,
子类只定义 DoSomethingInWC(),大大缩小了再也代码量。

  然而,倘诺我们先后中的Women和Man五个类为主没有共同代码,而且有一个PersonHandle类需要实例化他们,
而且不期待知道她们是男是女,而只需把他们当作人看待,并促成多态,那么定义成接口就有必不可少了。

  总而言之,接口与抽象类的分别重要在于运用的动机,而不在于其自己。
而一个事物该定义成抽象类仍旧接口,要按照现实条件的上下文决定。

  再者,我以为接口和抽象类的另一个分别在于,抽象类和它的子类之间应当是相似和特此外涉及,
而接口仅仅是它的子类应该实现的一组规则。(当然,有时也说不定存在一般与新鲜的关联,
但大家使用接口的目的不在这里)如,交通工具定义成抽象类,汽车、飞机、轮船定义成子类,
是能够接受的,因为汽车、飞机、轮船都是一种新鲜的交通工具。再譬如Icomparable接口,
它只是说,实现这么些接口的类必须要可以举行相比较,这是一条规则。假如Car这多少个类实现了Icomparable,
只是说,我们的Car中有一个主意可以对六个Car的实例进行相比较,可能是比哪辆车更贵,也说不定比哪辆车更大,
这都无所谓,但大家无法说“汽车是一种独特的可以相比”,这在文法上都不通。
 

 


概念:现在我们要支付一个利用,模拟移动存储设备的读写,即总括机与U盘、MP3、移动硬盘等设施举办数据沟通。

上下文(环境):已知要实现U盘、MP3播放器、移动硬盘二种运动存储设备,要求总结机能同这二种配备举行数据互换,
与此同时将来可能会有新的第三方的移动存储设备,所以总计机必须有扩大性,
能与近日一无所知而其后可能会产出的存储设备举办数据互换。各类存储设备间读、写的贯彻格局不同,
U盘和移动硬盘只有这多少个章程,MP3Player还有一个PlayMusic方法。

名词定义:数据互换={读,写}

来看下面的问题,我想各位脑子中毫无疑问有了累累设法,那是个很好解决的题目,很多方案都能达效用果。
下边,我列举几个出色的方案。

釜底抽薪方案列举


方案一:分别定义FlashDisk、MP3Player、MobileHardDisk六个类,实现各自的Read和Write方法。
接下来在Computer类中实例化上述五个类,为每个类分别写读、写方法。
譬如,为FlashDisk写ReadFromFlashDisk、WriteToFlashDisk五个方法。总共六个方法。

方案二:定义抽象类MobileStorage,在里面写虚方法Read和Write,
五个存储设备继承此抽象类,不分厚薄写Read和Write方法。
Computer类中涵盖一个档次为MobileStorage的积极分子变量,并为其编写get/set器,
如此Computer中只需要多少个点子:ReadData和WriteData,并通过多态性实现不同移动设备的读写。

方案三:与方案二基本相同,只是不定义抽象类,而是定义接口IMobileStorage,
举手投足存储器类实现此接口。Computer中经过信赖接口IMobileStorage实现多态性。

方案四:定义接口IReadable和IWritable,六个接口分别只含有Read和Write,
下一场定义接口IMobileStorage接口继承自IReadable和IWritable,剩下的落实与方案三一模一样。

下边,大家来分析一下之上四种方案:

首先,方案一最直白,实现起来最简便易行,可是它有一个致命的瑕疵:可扩大性差。
或者说,不相符“开放-关闭原则”(注:意为对扩大开放,对修改关闭)。
当未来有了第三方扩张移动存储设备时,必须对Computer举办修改。这就如在一个诚实的处理器上,
为每一种运动存储设备实现一个不等的插口、并分别有独家的驱动程序。当有了一种新的运动存储设备后,咱们将要将微机大卸八块,然后扩大一个新的插口,在编制一套针对此新装置的驱动程序。这种设计引人注目不可取。

此方案的另一个缺陷在于,冗余代码多。倘诺有100种运动存储,
个人档案,这我们的Computer中岂不是要起码写200个法子,这是不可能经受的!

我们再来看方案二和方案三,之所以将这多少个方案放在一起谈谈,
是因为她们基本是一个方案(从思想层面上的话),只然则实现手段不同,
一个是采用了抽象类,一个是选择了接口,而且最终达成的目标应该是均等的。

我们先来评价这种方案:首先它解决了代码冗余的问题,因为可以动态替换移动装备,
再就是都实现了一头的接口,所以随便有多少种运动装备,只要一个Read方法和一个Write方法,
多态性就帮我们解决问题了。而对第一个问题,由于能够运行时动态替换,而不自然移动存储类硬编码在Computer中,所以有了新的第三方设备,完全可以轮换进去运行。这就是所谓的“倚重接口,而不是依靠与实际类”,不信你看看,Computer类只有一个MobileStorage类型或IMobileStorage类型的成员变量,至于这一个变量具体是何许项目,它并不知道,这取决于我们在运作时给这多少个变量的赋值。如此一来,Computer和移动存储器类的耦合度大大降低。

那么这里该选抽象类如故接口呢?还记得第一篇小说我对抽象类和接口拔取的指出吗?看动机。
此地,我们的心劲显明是贯彻多态性而不是为着代码复用,所以自然要用接口。

最终大家再来看一看方案四,它和方案三很类似,只是将“可读”和“可写”多少个规则分别抽象成了接口,
下一场让IMobileStorage再持续它们。这样做,显明进一步提高了灵活性,不过,这有没有筹划过度的疑虑呢?
自我的见地是:这要看具体情状。假使大家的使用中或许会合世有的类,那个类只兑现读方法或只兑现写方法,
如只读光盘,那么如此做也是足以的。假设我们通晓以后现身的事物都是能读又能写的,
这这四个接口就从未有过必要了。其实只要将只读设备的Write方法留空或抛出分外,也可以不要这多少个接口。

总的说来一句话:理论是死的,人是活的,一切从实际需要来,防止设计不足,也要防备设计过度。

在此地,大家姑且认为今后的位移存储都是能读又能写的,所以我们选方案三。

实现


下面,我们要将迎刃而解方案加以落实。我拔取的语言是C#,不过在代码中不会用到C#有意的习性,
从而拔取其余语言的对象一样能够参照。

率先编写IMobileStorage接口:

Code:IMobileStorage

1namespace InterfaceExample
2{
3    public interface IMobileStorage
4    {
5        void Read();//从我读数据
6        void Write();//将数据写入自己
7    }
8}

代码相比简单,唯有多少个艺术,没什么好说的,接下去是多少个活动存储设备的切实可行落实代码:

U盘

Code:FlashDisk

1namespace InterfaceExample
2{
3    public class FlashDisk : IMobileStorage
4    {
5        public void Read()
6        {
7            Console.WriteLine(“Reading from FlashDisk……”);
8            Console.WriteLine(“Read finished!”);
9        }
10
11        public void Write()
12        {
13            Console.WriteLine(“Writing to FlashDisk……”);
14            Console.WriteLine(“Write finished!”);
15        }
16    }
17}
MP3

Code:MP3Player

1namespace InterfaceExample
2{
3    public class MP3Player : IMobileStorage
4    {
5        public void Read()
6        {
7            Console.WriteLine(“Reading from MP3Player……”);
8            Console.WriteLine(“Read finished!”);
9        }
10
11        public void Write()
12        {
13            Console.WriteLine(“Writing to MP3Player……”);
14            Console.WriteLine(“Write finished!”);
15        }
16
17        public void PlayMusic()
18        {
19            Console.WriteLine(“Music is playing……”);
20        }
21    }
22}
移动硬盘

Code:MobileHardDisk

1namespace InterfaceExample
2{
3    public class MobileHardDisk : IMobileStorage
4    {
5        public void Read()
6        {
7            Console.WriteLine(“Reading from MobileHardDisk……”);
8            Console.WriteLine(“Read finished!”);
9        }
10
11        public void Write()
12        {
13            Console.WriteLine(“Writing to MobileHardDisk……”);
14            Console.WriteLine(“Write finished!”);
15        }
16    }
17}
可以见到,它们都落实了IMobileStorage接口,人己一视写了各自不同的Read和Write方法。
下边,我们来写Computer:

Code:Computer

1namespace InterfaceExample
2{
3    public class Computer
4    {
5        private IMobileStorage _usbDrive;
6
7        public IMobileStorage UsbDrive
8        {
9            get
10            {
11                return this._usbDrive;
12            }
13            set
14            {
15                this._usbDrive = value;
16            }
17        }
18
19        public Computer()
20        {
21        }
22
23        public Computer(IMobileStorage usbDrive)
24        {
25            this.UsbDrive = usbDrive;
26        }
27   
28        public void ReadData()
29        {
30            this._usbDrive.Read();
31        }
32
33        public void WriteData()
34        {
35            this._usbDrive.Write();
36        }
37    }
38}
个中的UsbDrive就是可替换的位移存储设备,之所以用这么些名字,是为着让我们以为直观,
就像我们通常使用电脑上的USB插口插拔设备一样。

OK!上面我们来测试我们的“电脑”和“移动存储设备”是否工作例行。
本身是用的C#控制台程序,具体代码如下:

Code:测试代码

1namespace InterfaceExample
2{
3    class Program
4    {
5        static void Main(string[] args)
6        {
7            Computer computer = new Computer();
8            IMobileStorage mp3Player = new MP3Player();
9            IMobileStorage flashDisk = new FlashDisk();
10            IMobileStorage mobileHardDisk = new MobileHardDisk();
11
12            Console.WriteLine(“I inserted my MP3 Player into my
computer and copy some music to it:”);
13            computer.UsbDrive = mp3Player;
14            computer.WriteData();
15            Console.WriteLine();
16
17            Console.WriteLine(“Well,I also want to copy a great movie
to my computer from a mobile hard disk:”);
18            computer.UsbDrive = mobileHardDisk;
19            computer.ReadData();
20            Console.WriteLine();
21
22            Console.WriteLine(“OK!I have to read some files from my
flash disk and copy another file to it:”);
23            computer.UsbDrive = flashDisk;
24            computer.ReadData();
25            computer.WriteData();
26            Console.ReadLine();
27        }
28    }
29}
明日编译、运行程序,假如没有问题,将见到如下运行结果:

 

图2.1 各样活动存储设备测试结果

好的,看来我们的连串办事出彩。

后来……


刚过了一个星期,就有人送来了新的位移存储设备NewMobileStorage,让自家测试能不可以用,我微微一笑,
考虑这不是小菜一碟,让大家看看面向接口编程的威力吧!将测试程序修改成如下:

Code:测试代码

1namespace InterfaceExample
2{
3    class Program
4    {
5        static void Main(string[] args)
6        {
7            Computer computer = new Computer();
8            IMobileStorage newMobileStorage = new NewMobileStorage();
9
10            Console.WriteLine(“Now,I am testing the new mobile
storage:”);
11            computer.UsbDrive = newMobileStorage;
12            computer.ReadData();
13            computer.WriteData();
14            Console.ReadLine();
15        }
16    }
17}
编译、运行、看结果:

哈哈哈,神奇啊,Computer一点都不用改动,就可以使新的配备健康运作。这就是所谓“对扩张开放,对修改关闭”。

 

图2.2 新设施扩张测试结果

又过了几天,有人通告本人说又有一个叫SuperStorage的移位设备要收到大家的Computer上,
本人心想来吧,管你是“一流存储”依然“特级存储”,我的“面向接口编程大法”把你们统统搞定。

不过,当设备真的送来,我目瞪口呆了,开发那些新装置的公司尚未得到我们的IMobileStorage接口,
自然也绝非坚守这一个约定。这么些装置的读、写方法不叫Read和Write,而是叫rd和wt,
这下完了……不符合接口啊,插不上。可是,不要心急,我们再次回到现实来找找解决的主意。
咱俩一起想想:假诺您的Computer上只有USB接口,而有人拿来一个PS/2的鼠标要插上用,你该肿么办?
想起来了吗,是不是有一种叫“PS/2-USB”转换器的东西?也叫适配器,可以开展不同接口的转换。
对了!程序中也有转换器。

此处,我要引入一个设计格局,叫“艾达(Ada)pter”。它的机能就如现实中的适配器一样,
把接口不一样的六个插件接合起来。由于本篇不是讲设计情势的,而且艾达pter设计形式很好领会,
故此自己就不细讲了,先来看自己计划的类图吧:

如图所示,尽管SuperStorage没有实现IMobileStorage,但我们定义了一个贯彻IMobileStorage的SuperStorage艾达pter,
它聚合了一个SuperStorage,并将rd和wt适配为Read和Write,SuperStorage艾达(Ada)pter

 

图2.3 Adapter情势应用示意

现实代码如下:

Code:SuperStorageAdapter

1namespace InterfaceExample
2{
3    public class SuperStorageAdapter : IMobileStorage
4    {
5        private SuperStorage _superStorage;
6
7        public SuperStorage SuperStorage
8        {
9            get
10            {
11                return this._superStorage;
12            }
13            set
14            {
15                this._superStorage = value;
16            }
17        }
18   
19        public void Read()
20        {
21            this._superStorage.rd();
22        }
23
24        public void Write()
25        {
26            this._superStorage.wt();
27        }
28    }
29}
好,现在大家来测试适配过的新设备,测试代码如下:

Code:测试代码

1namespace InterfaceExample
2{
3    class Program
4    {
5        static void Main(string[] args)
6        {
7            Computer computer = new Computer();
8            SuperStorageAdapter superStorageAdapter = new
SuperStorageAdapter();
9            SuperStorage superStorage = new SuperStorage();
10            superStorageAdapter.SuperStorage = superStorage;
11
12            Console.WriteLine(“Now,I am testing the new super storage
with adapter:”);
13            computer.UsbDrive = superStorageAdapter;
14            computer.ReadData();
15            computer.WriteData();
16            Console.ReadLine();
17        }
18    }
19}
运作后会得到如下结果:

 

图2.4 利用Adapter格局运行新装置测试结果

OK!虽然遭逢了有些艰苦,可是在设计情势的拉扯下,我们仍然在尚未改动Computer任何代码的情状下促成了新装置的运转。

好了,理论在第一篇讲得丰硕多了,所以这边我就不多讲了。希望各位朋友结合第一篇的申辩和这一个事例,仔细牵记面向接口的问题。当然,不要忘了组合实际。

 
   
自动化(Automation)基础概念:二次开发接口(API)与插件(Addin)
2009-07-06 00:53 在前文,大家曾经表明了:
自动化(Automation)基础概念:COM组件(Component)与接口(Interface)
自动化(Automation)基础概念:变体(Variant)与Dispatch调用(IDispatch)
而与此同时,我们平常也恐怕时时听到以下这一个词语:
自动化(Automation,COM Automation) OA(办公自动化,Office Automation)

 二次开发接口(应用程序开发接口,Application Programming
Interface,API) 插件(Addin,Addon) 等等。
正文试图解释这多少个概念。
自动化(Automation)顾名思义是指“让机器在尚未人工干预的情景下活动完成一定的任务”。

为了成功这一目的,自动化(Automation)技术的基本想法是,应用程序(Application)

内需把团结的大旨效用以DOM模型的格局对外提供,使得别人可以透过这么些DOM模型来采纳该应用程序的功力。

那也就是大家常见说的应用程序编程接口——Application Programming
Interface,简称API。

为了与Windows
API这样的编程接口区分开来,我们引入一个专知名词,叫“二次开发接口”。

“二次开发”取意于“在现有应用程序基础上展开再付出”。其实假若你愿意把操作系统当作一个更大的应用程序的话,

二次开发接口和Windows
API并从未什么样很大的本色上的差距(即便大家领悟Windows
API并不是以COM组件情势提供的)。
 
知情了自动化(Automation),OA(办公自动化,Office
Automation)就相比好解释,无非是应用程序特指办公软件而已。
而OA是指办公(包括文件流转)系统的自动化。
在应用程序提供了编程接口(API)的前提下,典型气象下,我们有三种方法来利用这个API。

办法一是把应用程序当作一个Server,通过API对外提供劳务。在此意况下,应用程序只是当做一个EXE
COM Server的服务程序而已。

即使大家精通进程间的LPC或者RPC调用是怎么回事,那么所有就非凡Easy。方法二是兑现一个应用程序插件(Addin)。

这种方法更有意思一些。首先,这是一种进程内的调用,效率特别好。其次,这是一种双向的报道,

应用程序通过它提供的插件机制感知到插件的存在,并且将插件加载上来;插件则是在得到活动权后,

由此应用程序的API完成一定的效益。最后,也是最根本的,插件与应用程序融为一体,实际上是扩充了应用程序的能力,

使得应用程序变得更加强大。
插件(Addins)的启航过程大约如下:
 
应用程序启动。通过注册表(或者存放于任何任啥地方方)拿到插件列表。插件一般以
COM 组件形式提供,

故而只要有一个插件的 CLSID 或者 ProgID
的列表就足以了。其余,插件的效益可以出入,

不过他们需要统一实现一个接口,例如 _IDTExtensibility2
或者类似的东西。这一个接口在底下的第二步就用到了。

遍历插件列表,创设并初叶化各插件。关键是开头化。当然应用程序并不知道插件想做哪些,

它只是得到 _IDTExtensibility2(或者类似接口),调用其中的起始化函数(如
OnConnection)。

插件得到了开端化机会。注意,在开始化的时候,应用程序把自己的DOM模型的根接口(我们常见号称Application)传入。

在 _IDTExtensibility2 中,根接口被定义为 IDispatch 类型,即 IDispatch*
Application。

不过事实上可以更通用,如IUnknown* Application。有了这些 Application
指针,插件就可以为所欲为,

做它想做的事务,调用它想要调用的此外API。
从插件(Addins)展开来讲,可以讲那一个多的内容。不过这不是本文的用意。

之所以有关这地点的情节,大家不得不留待以后有时机继续这么些话题。可是我或者不由自主把话题起个起来:

由于插件(Addin)机制使得应用程序结构显得更为灵活,所以,越来越多的软件架构,

追求一种超轻量的基本(也就是我们说的应用程序,之所以称为内核,是因为它是团体所有的主导),

并把更多的法力通过插件(Addin)模式提供。超轻量的内核意味着需要缓解一个额外的关键点:

即使插件(Addin)不只是扩充应用程序的功用,也同时增添了应用程序的API,这个API与原有基础的API无缝地组成在一齐,

所以使得所有系统可以滚雪球一样越滚越大。 

 

admin

网站地图xml地图