Bowen Huang

学习,进步!记录学习的过程,分享知识。

导航

抽象类和接口的异同

Posted on 2012-08-06 22:14  Bowen Huang  阅读(1639)  评论(1编辑  收藏  举报

本文在文字说明的时候将不会举例,在文章写完后,在列出整个例子

一、抽象类:(abstract class)
    抽象类是特殊的类,只是不能被实例化;除此以外,具有类的其他特性;重要的是抽象类可以包括抽象方法,这是普通类所不能的。抽象方法只能声明于抽象类中,且不包含任何实现,派生类必须覆盖它们。另外,抽象类可以派生自一个抽象类,可以覆盖基类的抽象方法也可以不覆盖,如果不覆盖,则其派生类必须覆盖它们。抽象类不能被密封。 
 
  抽象类中virtual和abstract的异同
  相同点:两者是为了实现多态,都可以重写。
            对象都可以不调用子类的该方法。
  不同点:virtual在基类中必须有方法的实现,abstract必须只有方法的声明
            virtual在子类中可以不重写;abstract必须在子类中重写
 

二、接口:(interface)

  接口是引用类型的,类似于类

  接口有如下特性:接口除了可以包含方法之外,还可以包含属性、索引器、事件,而且这些成员都被定义为公有的。除此之外,不能包含任何其他的成员,例如:常量、域、构造函数、析构函数、静态成员。一个类可以直接继承多个接口,但只能直接继承一个类(包括抽象类)。

  使用C#接口应注意几个问题:

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

  2、接口和类都可以继承多个接口。

  3、类可以继承一个基类,接口根本不能继承类。C#的简化接口模型有助于加快应用程序的开发。

  4、一个接口定义一个只有抽象成员的引用类型。C#中一个接口实际所做的,仅仅只存在着方法标志,但根本就没有执行代码。这就暗示了不能实例化一个接口,只能实例化一个派生自该接口的对象。

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

  6、好的接口定义应该是具有专一功能性的,而不是多功能的,否则造成接口污染。如果一个类只是实现了这个接口的中一个功能,而不得不去实现接口中的其他方法,就叫接口污染。

  7、如果抽象类实现接口,则可以把接口中方法映射到抽象类中作为抽象方法而不必实现,而在抽象类的子类中实现接口中方法.

 

三、接口和抽象类的相似之处有四点:
       1、不能实例化,但可以声明它们的变量 ;
       2、包含未实现的方法声明;
       3、派生类必须实现未实现的方法,抽象类是抽象方法,接口则是所有成员(不仅是方法包括其他成员);

 

       4、接着通过这些变量来使用这些类型的成员,但不能直接访问派生对象的其它成员。

 

四、抽象类和接口的区别:

  1、派生类只能继承一个基类,即只能直接继承一个抽象类(但可以用一个继承链包含多个抽象类)。相反,类可以使用任意多个接口。

  2、抽象类可以拥有抽象成员和非抽象成员。

    抽象成员:没有代码体,且必须在派生类中执行,否则派生类本身必须也是抽象的。

    非抽象成员:拥有代码体,也可以是虚拟的,这样就可以在派生类中重写。

  3、接口成员必须都在使用接口的类上执行——它们没有代码体。

  4、接口成员被定义为公共的,但抽象类的成员也可以是私有的、受保护的、内部的或受保护的内部成员(其中受保护的内部成员只能在应用程序的代码或派生类中访问)。此外,接口不能包含字段、构造函数、析构函数、静态成员或常量。

  5、类是对对象的抽象,可以把抽象类理解为把类当作对象,抽象成的类叫做抽象类.而接口只是一个行为的规范或规定,微软的自定义接口总是后带able字段,证明其是表述一类类“我能做......”。抽象类更多的是定义在一系列紧密相关的类间,而接口大多数是关系疏松但都实现某一功能的类中.   

 

一些专家看法:

    dunai认为:抽象类是提取具体类的公因式,而接口是为了将一些不相关的类“杂凑”成一个共同的群体。至于他们在各个语言中的句法,语言细节并不是我关心的重点。

    Artech认为:所代码共用和可扩展性考虑,尽量使用Abstract Class。当然接口在其他方面的优势,我认为也不可忽视。

    shenfx认为:当在差异较大的对象间寻求功能上的共性时,使用接口;当在共性较多的对象间寻求功能上的差异时,使用抽象基类。

 

最后,有关C#接口和抽象类的学习,MSDN的建议:

    如果预计要创建组件的多个版本,则创建抽象类。抽象类提供简单易行的方法来控制组件版本。通过更新基类,所有继承类都随更改自动更新。另一方面,接口一旦创建就不能更改。如果需要接口的新版本,必须创建一个全新的接口。

    如果创建的功能将在大范围的全异对象间使用,则使用接口。抽象类应主要用于关系密切的对象,而接口最适合为不相关的类提供通用功能。

    如果要设计小而简练的功能块,则使用接口。如果要设计大的功能单元,则使用抽象类。

    如果要在组件的所有实现间提供通用的已实现功能,则使用抽象类。抽象类允许部分实现类,而接口不包含任何成员的实现。

 

网上的一个比喻:
  1.飞机会飞,鸟会飞,他们都继承了同一个接口“飞”;但是F22属于飞机抽象类,鸽子属于鸟抽象类。
  2. 就像铁门木门都是门(抽象类),你想要个门我给不了(不能实例化),但我可以给你个具体的铁门或木门(多态);而且只能是门,你不能说它是窗(单继承);一个门可以有锁(接口)也可以有门铃(多实现)。 门(抽象类)定义了你是什么,接口(锁)规定了你能做什么(一个接口最好只能做一件事,你不能要求锁也能发出声音吧(接口污染))。

 

例子:

  1、新建一个控制台应用程序ClassTest,新建类Animals

 

public abstract class Animals
{
        protected string _name;
        //声明抽象属性
        public abstract string Name { get; }
        //声明抽象方法
        public abstract void Show();
        //实现虚方法
        public virtual void MakeVoice()
        {
            Console.WriteLine("All animals can make voice.");
        }
        //实现一般的方法
        public void Sleep()
        {
            Console.WriteLine("All the animals need to sleep.");
        }
}

  2、新建一个接口IAction

public interface IAction
{
      void Move();
}

  3、新建一个Duck类:

    public class Duck : Animals, IAction {
        public Duck(string name)
        {
            _name = name;
        }
        //重写抽象属性
        public override string Name
        {
            get
            {
                return _name;
            }
        }
        //重写抽象方法,抽象方法必须重写
        public override void Show()
        {
            Console.WriteLine(_name + " is showing for you!");
        }
        //重写虚方法
        public override void MakeVoice()
        {
            Console.WriteLine("I am a duck,ga....ga....");
        }
        //实现接口方法
        public void Move()
        {
            Console.WriteLine("Duck also can swim.");
        }
    }

  4、新建一个Dog类:

    public class Dog : Animals, IAction {
        public Dog(string name)
        {
            _name = name;
        }
     //重写抽象属性
public override string Name { get { return _name; } }
     //重写抽象方法,抽象方法必须重写
public override void Show() { Console.WriteLine(_name + " is showing for you!"); }
//实现接口方法
public void Move() { Console.WriteLine("Dog also can run."); } }

  5、在Main函数中写代码:

        static void Main(string[] args)
        {
            Animals duck=new Duck("Duck");
            Console.WriteLine(duck.Name);
            duck.Sleep();
            duck.Show();
            duck.MakeVoice();//调用重写后的虚方法
            Console.WriteLine();

            Animals dog=new Dog("Dog");
            Console.WriteLine(dog.Name);
            dog.Sleep();
            dog.Show();
            dog.MakeVoice();//直接调用基类虚方法
            Console.WriteLine();

            Dog dog1=new Dog("Dog1");
            Console.WriteLine(dog1.Name);
            dog1.Show();
            Console.WriteLine();

            IAction dog2=new Dog("A big dog");
            dog2.Move();
            Console.WriteLine();
        }

  6、执行结果如下:

 

 

 

 

参考资料:

《C#语言参考》

《C#完全手册》

http://www.cnblogs.com/bear-study-hard/archive/2006/01/09/313673.html

http://blog.csdn.net/gz2008gz/article/details/4365971