c#中的interface abstract 与 virtual

c#中的interface abstract 与 virtual
2007-02-13 10:42
 

interface用来声明接口
1.只提供一些方法规约,不提供方法主体.  如:
public interface IPerson
{
    void getName();//不包含方法主体
}

2.方法不能用public abstract等修饰,无字段变量,无构造函数。
3.方法可包含参数。  如  
  public interface IPerson
  {
    void getAge(string s);
  }

  一个例子(例1):
public interface IPerson

   IPerson();              //错误
   string name;            //错误
   public void getIDcard();//错误

   void getName();         //right
   void getAge(string s);  //right
}

实现interface的类
1.与继承类的格式一致,如 public class Chinese:IPerson{}
2.必须实现 interface 中的各个方法

   例2,继承例1
public class Chinese:IPerson

   public Chinese(){}                  //添加构造
   public void getName(){}          //实现getName()
   public void getAge(string s){} //实现getAge()
}

abstract声明抽象类、抽象方法
1.抽象方法所在类必须为抽象类
2.抽象类不能直接实例化,必须由其派生类实现。
3.抽象方法不包含方法主体,必须由派生类以override方式实现此方法,这点跟interface中的方法类似

  如
public abstract class Book
{
  public Book()
  {   
  }

  public abstract void getPrice();      //抽象方法,不含主体
  public virtual void getName()   //虚方法,可覆盖
  {
      Console.WriteLine("this is a test:virtual getName()");
  }
  public virtual void getContent()   //虚方法,可覆盖
  {
      Console.WriteLine("this is a test:virtual getContent()");
  }
  public void getDate()                           //一般方法,若在派生类中重写,须使用new关键字
  {
      Console.WriteLine("this is a test: void getDate()");
   }
}

public class JavaBook:Book
{
      public override void getPrice()   //实现抽象方法,必须实现
      {
           Console.WriteLine("this is a test:JavaBook override abstract getPrice()");
      }
      public override void getName()   //覆盖原方法,不是必须的
      {
           Console.WriteLine("this is a test:JavaBook override virtual getName()");
      }
}

 测试如下:
public class test
{
   public test()
   {
    JavaBook jbook=new JavaBook();
         jbook.getPrice();      //将调用JavaBook中getPrice()
         jbook.getName();       //将调用JavaBook中getName()
         jbook.getContent();    //将调用Book中getContent()
         jbook.getDate();       //将调用Book中getDate()

    }
   public static void Main()
   {

       test t=new test();
   }
}

virtual标记方法为虚方法
1.可在派生类中以override覆盖此方法
2.不覆盖也可由对象调用
3.无此标记的方法(也无其他标记),重写时需用new隐藏原方法

abstract 与virtual : 方法重写时都使用 override 关键字

 

 

接口(interface)

简单地说接口就是一种对行为的契约或者规范。比如我们一说到“笔”,那么我们就知道它一定可以用来“书写”,而不管它是铅笔还是水笔,不管它是用木制的还是塑料制的。这里的“笔”就相当于一个契约(接口),它描述了“书写”这样一个行为。只要这个对象是“笔”,那么它就一定能“书写”(而不管对象具体是什么类型的东西)。正因为有了“笔”对“书写”行为的这样一个约定,所以当我们到商店里去买钢笔时,不会再问去售货小姐“这个东西能不能用来书写”;也不会在第一次用某种牌子的铅笔之前还要先看说明书才知道它能不能够书写。

 

而在我们的程序里,我们可以通过接口(interface)来制定这种契约。只要对象实现这个契约,那么这个对象就一定具有契约中所规定的行为,而不用去管这个对象到底是什么。当然你可能会说,那我这个对象表面上假装实现了这个契约,但在对象里面却不具有(实现)契约中所规定的行为(就像生活中的一些伪劣产品,广告里说实现了什么,但实际上都是假的),那么我们的编译器这个时候就充当明察秋毫的“质检人员”,把你的“伪劣”对象都找出来,不让你编译通过。所以编译器是我们使用接口的保证。

 

先来看看C#里关于interface的语法:

申明一个接口:

public interface

{

                 void 书写();       // 申明了书写行为,但仅仅是“打了个广告”而已,没有实现

}

注意:interface里申明的方法不需要诸如publicprivate等访问修饰符,因为interface里的方法都默认是public的。(秘密的规范还有什么意义呢?)

 

一个类被申明为实现某个接口的格式和继承很相似:

public class 钢笔 :

{

                        public void  书写()

                        {

                                    // 具体实现书写的过程

                        }

       }

所以也会经常说类“继承”了某个接口。

 

那么在程序里定义接口(interface)到底有用呢?这主要体现在3个方面。

 

1、当我们定义了自己的类,那么类的方法(即:行为)就是你自己定义的,你必须要通过其他形式(比如文档)来告诉其他的使用者,你的类有哪些方法,能实现什么。那么有了interface,我们可以在做自己的类之前先定义一个interface,然后让自己的类来遵守(实现)这个interface,那么你只需要把这个interface交给使用者就可以了,甚至在你的类还没有coding完成之前就可以先把interface交给使用者,这样你的类的coding和使用者coding的过程可以同时进行,从而提高开发速度。

 

2、在我们的程序里使用interface则可以获得更好扩充性和灵活性

来个例子(还是以笔为例):

public interface

{

        void书写();

}

 

public class  钢笔 :

{

        publicvoid书写()

        {

                ......          // 用钢笔自己的方式来实现到底怎么书写

        }   

        ......                  // 其他钢笔的属性和行为

}

 

public class  铅笔 :

{

        publicvoid书写()

        {

                ......          // 用铅笔自己的方式来实现到底怎么书写

        }   

        ......                  // 其他铅笔笔的属性和行为

}

 

 

public class 学生

{

        // 我们传入的参数是“笔”这个规范类型,而不是具体的钢笔类型或铅笔类型

        publicvoid写作文( tool) 

        {

                ......

                tool.书写();

                .....

        }

} 

 

……

public static main()

{

        钢笔  pen;                      // 申明“钢笔”的对象

        铅笔  pencil;                   // 申明“铅笔”的对象

 

        pen  = new钢笔();              // 实例“钢笔”对象实例

        pencil  = new铅笔();           // 实例“铅笔”对象实例

 

        学生  student = new学生();

 

        // 这里会有一个自动转型的过程,

        //会自动将penpencil向上转换成“笔”来符合 “写作文( tool)” 这个方法的参数规定

        student.写作文(pen)             // 学生用钢笔写作文

        student.写作文(pencil)          // 学生用铅笔写作文

        // 当以后发明出了“激光笔”,就让用学生用激光笔来写作文,

        //而不用去修改 学生.写作文( tool) 这个方法了(因为激光笔也会遵守“笔”的规范)。

}

 

从上面的例子可以看出来,我们的学生.写作文( tool) 这个方法使用了接口作为参数,这样只要实现了该接口的类型都可以传递进去,而不用管它具体是什么类型的,这使得我们的程序更容易扩充。同样凡是接受“笔”作为参数的方法,我们都可以动态的把“钢笔”或“铅笔”传进取(至于是传“钢笔”还是“铅笔”可以有很多技巧,比如用配置文件来标志)来动态的实现不同的效果和功能,这使得我们的程序更加灵活。

 

3、在类似C#Java这种单根继承的语言里,可以通过使用interface来在一定程度上实现多重继承的效果。

 

 

------------------------------------------------------------------------------------------------------------

最后还是说一说C#里运行时环境对接口的执行过程:

C#程序在运行中是如何使用接口的,如何访问接口函数,具体流程如下:

1、当调用一个接口的函数时,系统会去检查这个接口对应实例是什么;

2、找到这个实例后,再去找这个实例对应的实例类是什么(实例类,在虚函数一文里曾说明过);

3、根据这个实例类去检查该实例类是否和接口发生了捆绑(看是否实现了该接口,冒号后面就是);

4、好!如果实例类实现了该接口(发生了捆绑),那么它就在这个实例类中找到接口中所申明的方法的定义,然后执行该方法,然后结束。

5、如果没找到,它就继续往父类去找,直到找到第一个和接口捆绑的父类为止

6、找到后,它再检查这个父类里该方法是否是被定义为虚函数;

7、如果不是,他马上就执行这个方法,然后结束;

8、如果是,麻烦了,系统又要从头来过,去检查该最开始那个实例类里有否重载了父类里的这个虚函数…...(具体过程见上一篇《虚函数》)。

 

DEMO

interface I

{

        voidFunc();

}

 

class A : I

{

        publicvirtualvoidFunc()

        {

                Console.WriteLine("Func In A");

        }

}

 

classB : A , I  // 注意这里,用接口来实现类似多重继承的效果

{

        publicvoidFunc()

        {

                Console.WriteLine("Func In B");

        }

}   

 

class C : A

{

        publicoverridevoidFunc()

        {

                Console.WriteLine("Func In C");

        }

} 

 

class D :  A

{

        publicnewvoidFunc()

        {

                Console.WriteLine("Func In D");

        }

} 

   

publicstaticvoidmain()

{

        I a = newA() ;  //申明了接口a,并马上和一个类的实例发生关系了

        I b = newB() ;  //申明了接口b,并马上和一个类的实例发生关系了

        I c = newC() ;  //申明了接口c,并马上和一个类的实例发生关系了

        I d = newD() ;  //申明了接口d,并马上和一个类的实例发生关系了

 

        //检查a的实例类A,发现A和接口I捆绑了,所以执行A的函数Func ,结果: Func In A

a.Func();

 

        // 检查b的实例类B,发现B和接口I捆绑了,所以执行B的函数Func(而不会去执行父类A的,尽管A

        // 实现I接口),结果: Func In B         

        b.Func();

 

        // 检查c的实例类C,发现其没有和接口I捆绑,系统继续找它的父类. 发现AI捆绑了,他就去找

        // 函数A,发现A是虚拟函数,系统又从头来找类的实例C,发现C重载(override)Func,好了,

        //上执行该函数. 结果是Func In C

        c.Func();

 

        // 检查d的实例类D,发现其没有和接口I捆绑,系统继续找它的父类. 发现AI捆绑了,他就去找

        // 函数A,发现A是虚拟函数,系统又从头来找类的实例D,但是D里没有重载(override)Func(而是

        // new覆盖了),所以又会到D的父类里找,所以还是执行AFunc(),结果是Func In A

        d.Func() ;      

}

 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值