zoukankan      html  css  js  c++  java
  • 五、抽象基类与接口

    5.1 抽象类与抽象方法

         在一个类前面加上“abstract”关键字,此类就成为了抽象类。

         对应的,一个方法类前面加上“abstract”关键字,此方法就成为了抽象方法。

    abstract class Fruit   //抽象类

    {

         public abstract void GrowInArea();  //抽象方法

    }

         注意抽象方法不能有实现代码,在函数名后直接跟一个分号。

         抽象类专用于派生出子类,子类必须实现抽象类所声明的抽象方法,否则,子类仍是抽象类。

         抽象类一般用于表达一种比较抽象的事物,比如前面所说的“水果”,而抽象方法则说明此抽象类应该具有的某种性质,比如Fruit类中有一个抽象方法GrowInArea(),说明水果一定有一个最适合其生长的地区,但不同的水果生长地是不同的。

         从同一抽象类中继承的子类拥有相同的方法(即抽象类所定义的抽象方法),但这些方法的具体代码每个类都可以不一样,如以下两个类分别代表苹果(Apple)和菠萝(Pineapple):

    class Apple:Fruit  //苹果

    {

         public override void GrowInArea()

         {

              Console.WriteLine("南方北方都可以种植我。");

         }

    }

    class Pineapple:Fruit   //菠萝

    {

         public override void GrowInArea()

         {

              Console.WriteLine("我喜欢温暖,只能在南方看到我。");

         }

    }

         注意上述代码中的override关键字,这说明子类重写了基类的抽象方法。抽象类不能创建对象,一般用它来引用子类对象。

    Fruit f;

    f=new Apple();

    f.GrowInArea();

    f=new Pineapple();

    f.GrowInArea();

         运行结果:

    南方北方都可以种植我。

    我喜欢温暖,只能在南方看到我。

         注意同一句代码“f.GrowInArea();”会由于f所引用的对象不同而输出不同的结果。可以看到,代码运行结果类似于上一节介绍的“虚方法调用”,两者没有本质差别。

         可以按照以下公式编写代码:

         抽象类 抽象类变量名=new 继承自此抽象类的具体子类名();

         一个抽象类中可以包含非抽象的方法和字段。因此:

         包含抽象方法的类一定是抽象类,但抽象类中的方法不一定是抽象方法。

    5.2 抽象属性

         除了方法可以是抽象的之外,属性也可以是抽象的,请看以下代码:

    abstract class Parent

    {

         public abstract String Message   //抽象属性

         {

              get;

              set;

         }

    }

    class Child:Parent

    {

         private String _msg;

         public override String Message

         {

              get

              {

                   return _msg;

              }

              set

              {

                   _msg=value;

              }

         }

    }

         使用代码:

    Parent p=new Child();

    p.Message="Hello";

    5.3 接口

         来看以下这句话:

    鸭子是一种鸟,会游泳,同时又是一种食物。

         如何在面向对象的程序中表达这种关系?

         如果使用C++,可以设计成让鸭子(Duck)类继承自两个父类(鸟Bird和食物Food)。但在C#中所有的类都只能有一个父类,此方法不可行。

         为了解决这一问题,C#引入了接口(interface)这一概念,并规定“一个类可以实现多个接口”。

    (1)接口的定义与使用

         关键字interface用于定义接口:

    //定义两个接口

    public interface  ISwim

    {

         void Swim();

    }

    public interface  IFood

    {

         void Cook();

    }

         接口可以看成是一种的抽象类,它的所有方法都是抽象方法。

         可以用与继承相同的语法定义一个类实现某些接口:

    //定义一个抽象类

    public abstract class  Bird

    {

         public abstract void  Fly();

    }

    //继承自一个抽象类,实现两个接口

    public class Duck : Bird, IFood, ISwim

    {

         //实现ISwim接口

         public  void  Swim()

         {

              Console.WriteLine("是鸭子就会游泳");

         }

         //实现IFood接口

         public void  Cook()

         {

              Console.WriteLine("鸭子经常被烧烤,北京烤鸭就很有名");

         }

         //实现抽象类Bird中的抽象方法

         public override void  Fly()

         {

              Console.WriteLine("只有野鸭才会飞");

         }

    }

         可以看到,抽象类定义了对象所属的类别,而接口实际上定义了一种对象应具有的行为特性。

         可按以下公式使用接口:

         接口类型名 变量名=new 实现了接口的类型名();

         示例代码如下:

    static void Main(string[] args)

    {

         Duck d = new  Duck();

         //Duck对象d可以使用3种方法:

         //1.自身定义的;

         //2.父类定义的

         //3.接口定义的

         d.Fly();

         d.Cook();

         d.Swim();

         //将子类(Duck)对象赋给基类变量

         Bird b = d;

         //现在只能使用基类定义的Fly()方法

         b.Fly();

         //将Duck对象赋给ISwin接口变量

         ISwim s = d;

         //现在只能使用接口定义的Swim()方法

         s.Swim();

         //将Duck对象赋给另一个实现的接口IFood接口变量

         IFood f = d;

         //现在只能使用接口定义的Cook()方法

         f.Cook();

    }

         请读者仔细地阅读上述代码的注释,由于 Duck类继承自抽象基类  Bird,又实现了  ISwim和 IFood两个接口,所以,Duck对象拥有这三者所定义的所有方法,并且可以赋值给这三种类型的变量。

         需要注意的是,虽然程序中始终都只有一个 Duck对象,但将其赋值给不同类型的变量后,其可以使用的方法是不一样的。

    (2)显式实现接口

         上面讲到,某个类可以实现多个接口,当创建一个此类的对象之后,通过引用这个对象的对象变量可以访问其所有的公有方法(包括自身的公有方法以及由接口定义的公有方法

    以)。在这种情况下,根本分不清哪些方法是由接口定义的,哪些是由类自己定义的。C#提供了一种显式接口实现机制,可以区分开这两种情况,一个示例代码如下:

    interface  IMyInterface

    {

         void func();

    }

    public class  A:IMyInterface

    {

         void IMyInterface.func()

         {

              //……

         }

         public void func2()

         {

              //……

         }

    }

         请注意在方法 func前以粗体突出显示的接口名称,这就是  C#对接口 IMyInterface的显式实现方式。

         当类 A显式实现接口  IMyInterface之后,只能以下面这种方式访问接口定义的方法:

    IMyInterface  a = new  A();

    a.func();

         以下代码将不能通过编译:

    A  a = new  A();

    a.func();

         由此得到一个结论:

         如果一个类显式实现某个接口,则只能以此接口类型的变量为媒介调用此接口所定义的方法,而不允许通过类的对象变量直接调用。

         或者这样说:

         被显式实现的接口方法只能通过接口实例访问,而不能通过类实例直接访问。

  • 相关阅读:
    IdentityServer4学习笔记
    常用链接地址
    c++ 多线程(2000个)端口扫描(附源码)
    C++ 定时器Timer在项目中的使用
    [Under the hood]---Matt Pietrek October 1996 MSJ
    [under the hood]Reduce EXE and DLL Size with LIBCTINY.LIB
    TN035: Using Multiple Resource Files and Header Files with Visual C++
    单文件版本的netframework的net core 2.1
    Dependency Walker的替代品Dependencies
    怎么使用gradle给spring 打thinjar(gradle 7)
  • 原文地址:https://www.cnblogs.com/mxx0426/p/4300350.html
Copyright © 2011-2022 走看看