接口这个概念一点都不新鲜,接触了C++,C#,Java等主流语言都包含了接口这个让人爱让人很的小东东。

    让人爱是它能帮助我们完成一些相对复杂的操作和逻辑关系的保持,尤其是在包含很多复杂类型和继承关系的情况下。让人恨是因为它看起来并不像表面那样简单易用,其中涉及到了诸多的访问控制,再加上接口间的继承和派生。所以简单写一个接口总结,权当读书笔记,好记性不如烂笔头。

    先来做点知识储备的功课。


    子类和基类以及他们的继承关系。废话少说 上代码

   

  1. public class Animal 
  2.     public int NumberOfLegs; 
  3.     public void Eat() 
  4.     { 
  5.         //code to make the animal eat 
  6.     } 
  7.  
  8. public class Bird : Animal 
  9.     public double Wingspan; 
  10.     public void Fly() 
  11.     { 
  12.         // code to make the bird fly 
  13.     } 
  14.  
  15. public button1_Click(Object sender, EventArgs e) 
  16.     Bird tweety = new Bird(); 
  17.     tweety.Wingspan = 7.5; 
  18.     tweety.Fly(); 
  19.     tweety.NumberOfLegs = 2; 
  20.     tweety.Eat(); 

    类的继承用冒号来实现,跟在冒号后面的是基类名。注意一点,C#当中不允许多继承也就是说一个子类只能从继承自一个父类。这也好理解,就好比你只能有一个亲生老爹一样。子类继承了父类就自动获得了父类中所有的public和protect方法和字段。父类中private修饰的方法和字段对子类来说是不可见的。父类中protect对于子类是可见的,对于其他类是不可见的。当一个类被sealed修饰时(),说明他是不可被其他类所继承的,用太监来形容它我感觉是很贴切的。

    子类对于父类的继承不仅仅是方法和字段的获取,最重要的是保证一个逻辑关系的向下延伸,多个子类拥有一个父类,他们都自动的继承了父类的一些属性。同时他们之间也有不同,就像兄弟几个只是长得像,但是肯定还是各自有各自的特点的(当然了 我们不排除双胞胎的特殊情况,不过在程序设计当中这么做似乎没有任何意义)。好了,那么如何能保证兄弟之间的差异呢?这就仰仗于子类覆盖或替换父类的方法。

    父类中被virtual关键字修饰的方法在子类中都可以被重写,虽然方法名是一样的,但是各个子类都有着不同的功能。子类中必须用override来修饰准备替换的方法。

 

  1. public class Bird 
  2.     public virtual void Fly() 
  3.     { 
  4.         // code to make the bird fly 
  5.     } 
  6.  
  7. public class Penguin : Bird 
  8.     public override void Fly() 
  9.     { 
  10.         MessageBox.Show("Penguins can't fly!"); 
  11.     } 

    鸟类都有一个共同的方法Fly(),但并不是所有的鸟都能飞,比如企鹅,鸵鸟。重写就很好的解决了这一问题,既保证了鸟类和企鹅之间的类继承关系,同时也解决了不同鸟之间的差异问题。

    下面介绍一下基类对于子类的访问。在程序设计时我们可以用父类的引用来指向子类的实例。当然这么做会有一定的缺点,子类对于父类是透明的。父类并不知道谁继承了他,更不知道子类当中又新增了什么字段和方法。所以父类的引用只能访问那些在父类当中已经存在的字段和方法。乍看起来有了这样的限制,父类引用指向子类实例好像就没什么实际意义了。但是我们在实际应用中确实时常用到这个小技巧的。

    我们可以用一个基类引用的数组来保存子类的实例,来访问他们父类中共同的方法和字段。同样的方法也适用于接口,我们下面会说到。

   写子类时要注意一点,父类如果有自己定义的构造函数,那子类也必须有构造函数。如果父类有多个构造函数那么子类至少有一个。父类的构造函数在子类构造之前执行。这个很好理解。

   下面开始介绍接口。

 


   

    首先要说明的就是接口也是一个类,只不过他比平时我们用到的类特别一些。原因主要有以下几点:

  1. 接口中只有函数的定义,没有实现。
  2. 接口中没有变量的定义,但是可以定义属性。
  3. 接口没有构造函数,而且接口中的函数不需要访问修饰关键字修饰。都是public的。
  4. 由于接口没有构造函数,而且编译器也不自动生成默认的构造函数,所以接口不能被实例化,但是可以被继承。

    接口只能被类所实现,而且实现这个接口的类必须实现接口中所有的方法。接口就像一个契约一样,保证该类具有接口中描述的特性。

   接口更像是对一个类功能的补充。每当一个类实现了这个接口,那么就可以用这个接口的引用来指向那个类的实例。当然了,通类的继承一样,接口引用只能访问接口中含有的方法和属性。同时一个类可以实现多个接口,当然责任是必须实现所有接口当中的方法。就像一个人可以有很多个干爹一样。

    简单说一下接口的继承,和类的继承没有太大差别。基接口的引用可以指向实现了子接口的类的实例。想一想这样做的实际意义。可以把接口看做是一个附加的功能。很多不同的类都可以拥有这个功能,各个类之间不一定都有继承关系。但是他们都可以用一个接口引用的数组来访问(仅仅能访问接口中的方法和属性)。

  


 

    之前介绍的父子关系,包括类和接口,都是由下向上的转换。下面介绍一种特殊的方法,可以让这种关系倒置。父一样可以操作子中的方法。

   实现这一功能需要用到的是is和as关键字。

  1. public interface Isendmail 
  2.     //一个接口,附加了送信的功能 
  3.     void sendmail(); 
  4. public class Pigeon : Bird, Isendmail 
  5.     // ...... 
  6.     void sendmail() 
  7.     { 
  8.         // send mail... 
  9.     } 
  10. Bird[] bir = new Bird[2]; 
  11. bir[0] = new Penguin(); 
  12. bir[1] = new pigeon(); 
  13.  
  14. forint i =0; i<bir.Length; i++) 
  15.     if(bir[i] is Isendmail) 
  16.     { 
  17.         Isendmail mail; 
  18.         mail = bri[i] as Isendmail; 
  19.         mail.sendmail(); 
  20.     } 

    特别注意接口引用只能访问接口当中声明的方法和属性。

    目前也就这么多,有机会把虚类也总结起来。