【虚方法】

virtual关键字用于在基类中修饰方法(或属性、索引器或事件声明),并且允许在派生类中重写这些对象(即override可写可不写)。

virtual的使用会有两种情况:
 情况1:在基类中virtual方法在子类中没用override重写。那么在对子类实例的调用中,该虚方法使用的是基类定义的方法。
 情况2:在基类中virtual方法在派生类中使用override重写。那么在对子类实例的调用中,该虚方法使用的是子类重写的方法。

 

【抽象方法】

 abstract关键字只能用在抽象类中修饰方法,并且没有具体的实现(没有方法体),抽象方法必须放在抽象类中(但抽象类中可以没有抽象方法)
 非抽象类的派生类必须全部实现父类的抽象方法和抽象访问器,使用override关键字来实现。

 

【接口】

 接口用于描述一组类的公共方法/公共属性
 接口中的方法没有具体实现,也就是没有方法体必须由继承者去实现而且必须全部实现
 接口中的方法不需要修饰符,默认就是公有的(Default / Public)
 接口可以包含方法、属性、索引器、事件。不能包含任何其他的成员,例如:常量、字段、域、构造函数、析构函数、静态成员

 

【虚方法 VS 抽象方法】

虚方法和抽象方法都可以供派生类重写
 1. 虚方法必须有实现部分,抽象方法没有提供实现部分,抽象方法是一种强制派生类覆盖的方法,否则派生类将不能被实例化;而虚方法提供了选择,可以覆盖可以不覆盖。
 2. 抽象方法只能在抽象类中声明,虚方法不是。

 

【抽象 VS 接口】

 抽象类和接口都不能实例化(无法New创建实例,只能被继承
 抽象类可以包含非抽象方法,而接口只能包含抽象方法
 一个类可以实现多接口,单继承。(可以继承多个接口,但只能继承单个类)


在学习c#接口(interface)和抽象类(abstract)的时候 发现接口和抽象类极为相似,比如说:继承后都需要重写,或者都不能通过new关键字来实例化… ,那么他们之间到底有什么区别与联系 ? 

一、接口的特征

​ 1、接口使用interface关键字声明

​ 2、接口中的成员有属性、方法、事件,索引器并且都没有实现部分,可以说是没有方法体

​ 3、接口中不能声明字段和常量接口中的成员不能使用任何修饰符

​ 4、继承接口的类或结构必须隐式或显式实现接口中的所有成员

​ 5、一个接口允许继承多个接口,一个也可以继承多个接口,但是只能继承一个基类。

       C#不允许多重类继承(子类继承多个父类),但允许多重接口实现(子类继承多个父接口)。

​ 6、接口不能通过new关键字来实例化

​ 7、类或结构继承接口或不用使用override关键字即可实现

​ 8、接口中没有实例构造函数

1 public interface IPict{
 2     void DisplayImage();
 3 }
 4 
 5 public interface IPictManip{
 6     void DisplayImage();
 7 }
 8 
 9 public class MyImages: IPict, IPictManip{
10     void IPict.DisplayImage(){    //如果用显式接口实现方法,则不需使用访问修饰符
11         Console.WriteLine("DisplayImage的IPict实现");
12     }
13     void IPictManip.DisplayImage(){
14         Console.WriteLine("DisplayImage的IPictManip实现");
15     }
16 }
17 
18 static void Main(string[] args){
19     MyImages objM = new MyImages();

20     IPict Pict = objM;  //IPict引用
21     Pict.DisplayImage();

22     IPictManip PictManip = objM;  //IPictManip引用
23     PictManip.DisplayImage();

24 }

 

二、抽象类的特征

​ 1、抽象类中允许有实例方法,也就是说抽象类中除了抽象方法以外,其他的成员允许有具体的实现

​ 2、抽象类中的抽象方法没有实现部分,也就是说没有方法体,并且继承此抽象类的非抽象类必须使用override关键字来重写抽象类中的所有方法

​ 3、在抽象类中可以声明任何类成员,并且类成员可以使用任意的修饰符 (接口不行)

​ 4、抽象类仅能让非抽象类和抽象类继承

​ 5、非抽象类继承抽象类后必须重写(实现)抽象类中的所有抽象方法抽象类继承抽象类后可以只实现部分抽象方法

 

三、总结

1、接口与抽象类的相同点

​ (1)都不能使用new关键字来实例化

​ (2)成员方法都没有实现部分,或者说都没有方法体

​ (3)继承他们的,都必须实现他们的成员方法

 

2、接口和抽象类的不同点

接口(interface)抽象类(abstract)
接口中不能声明字段和常量,接口成员有:属性、方法、事件、索引器抽象类中可以声明任何类成员
在接口中只能定义成员,但不能具体实现。(没有方法体)在抽象类中除了抽象方法以外,其他成员允许有具体的实现
接口中没有实例构造函数,也就是说没有构造函数抽象类中有构造函数
接口成员不能使用任何访问修饰符抽象类中的类成员可以使用任意的访问修饰符
继承接口的类或结构必须隐式或显式实现接口中的所有成员否则需要将实现类定义为抽象类,并将接口中未实现的成员以抽象的方式实现继承抽象类的类必须重写实现抽象类中的所有抽象方法,或者抽象类继承抽象类,可以重写部分抽象方法
接口不能作为派生类继承抽象类可以继承非抽象类或抽象类
接口可以作为基类来多继承:接口、类和结构抽象类可以作为基类只能实现单继承,只能让非抽象类或者抽象类继承

 

抽象类(abstract)和接口(interface)的实现

抽象类


  抽象方法是没有代码实现的方法,使用abstract关键字修饰;

  1. 抽象类是包含0到多个抽象方法的类,其不能实例化。含有抽象方法的类必须是抽象类,抽象类中也可以包含非抽象方法;
  2. 重写抽象类的方法用override关键字。

 

 1 //定义一个抽象类,包含一个抽象方法,但该方法未实现
 2 abstract class MyAbs{
 3     public abstract void AbMethod();            
 4 }
 5 //定义一个非抽象派生类,只能继承一个类
 6 class MyClass:MyAbs{
 7      public override void AbMethod(){
 8         Console.WriteLine("此MyClass中实现父类中未实现的抽象方法!");
 9      }
10  }
11 //在主程序中实例化一个MyClass对象,并调用AbMethod方法
12 static void Main(string[] args){
13     MyClass objMyClass = new MyClass();
14     objMyClass.AbMethod();
15 }

 

 

 

  • 虚方法(virtual)与抽象方法(abstract)的区别
  1. 虚方法必须要有方法体(方法体可以没有内容,但必须保留大括号),抽象方法不允许有方法体(不能有大括号);
 父类的虚方法:
        protected virtual void Initialization()
        {

        }

子类的方法重写:

    protected override void Initialization()
    {
        MsgTypeName = NAME;
        LoadChildrenUIFormList = new List<string> { "TrainingGround_SkillInfoBoxUI" };
        CurrentUIType.UIForms_ShowMode = UIFormShowMode.HideOther;
    }

     2. 虚方法可以被子类重载(override)(也可以不重载,在对子类实例的调用中,该虚方法使用的是基类定义的方法),抽象方法必须被子类重载(必须重新继承实现)

 

    3.虚方法除了在 (sealed) 密封类中都可以写,抽象方法只能在抽象类中写(抽象类中不是必须要有抽象方法,抽象方法可以为0个)。

 

 

接口


  接口是一套规范,遵守这个规范就可以实现功能。

  1. 接口中只定义方法的原型不能有字段和常量
  2. 继承接口的类 必须实现接口中所有的方法 才能实例化

复制代码

1 //隐式声明为public
2 public interface IPict{
3     //只有方法声明,没有访问修饰符,没有实现
4     int DeleteImage();
5     void DisplayImage();
6 }

复制代码

  定义派生自接口的类,并实现所有接口中的方法

复制代码

 1 public class MyImages: IPict{
 2     //第一个方法的实现
 3     public int DeleteImage(){
 4         Console.WriteLine("DeleteImage实现!");
 5     }
 6     
 7     //第二个方法的实现
 8     public void DisplayImage(){
 9         Console.WriteLine("DisplayImage实现!");
10     }
11 }

复制代码

  在主程序中实例化一个MyImages对象,并调用DeleteImage和DisplayImage方法

复制代码

1 static void Main(string[] args){
2     MyImages ofjM = new MyImages();
3     objM.DisplayImage();
4     int t = objM.DeleteImage();
5     Console.WriteLine(t);
6 }

复制代码

 

  • 多重接口实现

  C#不允许多重类继承(子类继承多个父类),但允许多重接口实现(子类继承多个父接口)。但如果发生命名冲突就需要使用前缀进行显式接口实现或调用。如果继承接口的类中用显示方法实现接口中的方法时,实现方法不需加访问修饰符(public)

复制代码

 1 public interface IPict{
 2     void DisplayImage();
 3 }
 4 
 5 public interface IPictManip{
 6     void DisplayImage();
 7 }
 8 
 9 public class MyImages: IPict, IPictManip{
10     void IPict.DisplayImage(){    //如果用显式接口实现方法,则不需使用访问修饰符
11         Console.WriteLine("DisplayImage的IPict实现");
12     }
13     void IPictManip.DisplayImage(){
14         Console.WriteLine("DisplayImage的IPictManip实现");
15     }
16 }
17 
18 static void Main(string[] args){
19     MyImages objM = new MyImages();

20     IPict Pict = objM;  //IPict引用
21     Pict.DisplayImage();

22     IPictManip PictManip = objM;  //IPictManip引用
23     PictManip.DisplayImage();

24 }

复制代码

 

  • 使用自定义接口

  接口作为参数使用:接口作为参数传递了实现接口的对象

复制代码

1 //无论谁收作业参数类型部分都不需做任何改变
2 private void DoCollectHomework(IHomeworkCollector collector){
3     collector.CollectHomework();
4 }
5 
6 DoCollectHomework(scofield);

复制代码

  接口作为返回值使用:接口作为返回值返回了一个实现接口的对象

复制代码

 1 private IHomeworkColletor CreateHomeworkCollector(string type){
 2     switch(type){
 3         case "student":
 4         collector = new Student("Scofield", Genders.Male, 28, "越狱");
 5         break;
 6     }
 7     //返回一个实现该接口的对象
 8     return collector
 9 }
10 
11 collector.CollectHomework();

 

 

 

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐