zoukankan      html  css  js  c++  java
  • [.net 面向对象编程基础] (13) 面向对象三大特性——多态

    [.net 面向对象编程基础] (13) 面向对象三大特性——多态

          前面两节,我们了解了面向对象的的封装和继承特性,面向对象还有一大特性就是多态。比起前面的封装和继承,多态这个概念不是那么好理解。我们还是从一个事例开始:

          公司最近为了陶冶情操,养了几种动物(Animal),有猫(Cat)、狗(Dog)、羊(Sheep),这些动物都有共同的特性,会吃(Eat)、会叫(Shout),但是它们吃的不同,叫的也不同。既然这样,我们能不能设计一个动物类(Animal)和它的成员(Eat方法、Shout方法)来表示这些动物的共同特征,而当我们关注猫时,猫来实现这两个成员(吃鱼、喵喵叫);当我们关注狗时,狗来实现这两个成员(吃肉和汪汪叫)。

    1.什么是多态?

    上述例子就是一个典型的多态,就是父类的一些成员,子类继承后去重写从而实现不同的功能。

    多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。这就是多态,这种特性称为多态性。

    2.多态的分类

    多态性分为两种,一种是编译时的多态性,一种是运行时的多态性。

    编译时的多态性:编译时的多态性是通过重载来实现的。对于非虚的成员来说,系统在编译时,根据传递的参数、返回的类型等信息决定实现何种操作。

    运行时的多态性:运行时的多态性就是指直到系统运行时,才根据实际情况决定实现何种操作。C#中运行时的多态性是通过覆写虚成员实现。 

    3.多态的实现

      我们知道多态有两种,一种是编译时通过重载实现,另一种是运行时,通过重写或叫覆写来实现,那么如何实现他们?

    3.1编译时多态:重载(overload

    重载(overload):重载指的是同一个类中有两个或多个名字相同但是参数不同的方法,(:返回值不能区别函数是否重载),重载没有关键字。

    注意:

    A.从重载的定义来看,重载是一种编译时多态

    B.重载不需要事先定义可重载的方法,即没有关键字

    C.重载只是针对一个类内部的几个参数不同,名称相同的方法。

    我们还在本节开篇那几只陶冶情操的动物来示例说明,代码如下:

     1 /// <summary>
     2 /// 狗(多态:重载事例)
     3 /// </summary>
     4 class Dog
     5 {
     6     /// <summary>
     7     /// 8     /// </summary>
     9     public void Shout()
    10     {
    11         Console.WriteLine("汪!");
    12     }
    13 
    14     /// <summary>
    15     /// 叫(重载方法)
    16     /// </summary>
    17     public void ShoutCount(int count)
    18     {
    19         int i = 0;
    20         string shout = "";
    21         do
    22         {
    23             shout += "汪!";
    24             i++;
    25         } while (i <= count);
    26         Console.WriteLine(shout);
    27     }
    28 }
    //调用
    Dog dog = new Dog();
    dog.Shout();
    dog.ShoutCount(5);

    3.2运行时多态:重写

    重写有两种,一种是override修饰符,另一种使用new修饰符,下面会举例说明两种重写的使用方法和异同。

    重写(override:也称过载,重写是指子类对父类中虚函数或抽象函数的“覆盖”(这也就是有些书将过载翻译为覆盖的原因),但是这种“覆盖”和用new关键字来覆盖是有区别的。

    下面以本节开题前例子,实现重写,代码如下:

      1     /// <summary>
      2     /// 动物类(父类)
      3     /// </summary>
      4     class Animal
      5     {
      6        /// <summary>
      7        /// 名字
      8        /// 说明:类和子类可访问
      9        /// </summary>
     10        protected string name;
     11 
     12 
     13         /// <summary>
     14         /// 构造函数
     15         /// </summary>
     16         /// <param name="name"></param>
     17         public Animal(string name)
     18         {
     19             this.name=name;
     20         }
     21 
     22         /// <summary>
     23         /// 名字(虚属性)
     24         /// </summary>
     25         public virtual string MyName
     26         {
     27             get { return this.name; }           
     28 
     29         }
     30 
     31         /// <summary>
     32         /// 吃(虚方法)
     33         /// </summary>
     34         public virtual void Eat()
     35         {
     36             Console.WriteLine("我会吃!");
     37         }
     38 
     39         /// <summary>
     40         /// 叫(虚方法)
     41         /// </summary>
     42         public virtual void Shout()
     43         {
     44             Console.WriteLine("我会叫!");
     45         }
     46     }
     47 
     48     /// <summary>
     49     /// 狗(子类)
     50     /// </summary>
     51     class Dog:Animal
     52     {
     53         string myName;
     54         public Dog(string name): base(name)
     55         {
     56             myName = name;
     57         }
     58         
     59         /// <summary>
     60         /// 名字(重写父类属性)
     61         /// </summary>
     62         public override string MyName
     63         {
     64             get { return "我是:狗狗,我叫:"+this.name; }
     65 
     66         }
     67 
     68 
     69         /// <summary>
     70         /// 吃(重写父类虚方法)
     71         /// </summary>
     72         public  override void Eat()
     73         {
     74             Console.WriteLine("我喜欢吃肉!");
     75         }
     76 
     77         /// <summary>
     78         /// 叫(重写父类方法)
     79         /// </summary>
     80         public override void Shout()
     81         {
     82             Console.WriteLine("汪!汪!汪!");
     83         }
     84     }
     85     /// <summary>
     86     /// 猫(子类)
     87     /// </summary>
     88     class Cat : Animal
     89     {
     90         string myName;
     91         public Cat(string name)
     92             : base(name)
     93         {
     94             myName = name;
     95         }
     96         /// <summary>
     97         /// 名字(重写父类属性)
     98         /// </summary>
     99         public override string MyName
    100         {
    101             get { return "我是:猫咪,我叫:" + this.name; }
    102 
    103         }
    104 
    105         /// <summary>
    106         /// 吃(重写父类虚方法)
    107         /// </summary>
    108         public override void Eat()
    109         {
    110             Console.WriteLine("我喜欢吃鱼!");
    111         }
    112 
    113         /// <summary>
    114         /// 叫(重写父类方法)
    115         /// </summary>
    116         public override void Shout()
    117         {
    118             Console.WriteLine("喵!喵!喵!");
    119         }
    120     }
    121 
    122     /// <summary>
    123     /// 羊(子类)
    124     /// </summary>
    125     class Sheep : Animal
    126     {
    127         string myName;
    128         public Sheep(string name)
    129             : base(name)
    130         {
    131             myName = name;
    132         }
    133         /// <summary>
    134         /// 名字(重写父类属性)
    135         /// </summary>
    136         public override string MyName
    137         {
    138             get { return "我是:羊羊,我叫:" + this.name; }
    139 
    140         }
    141 
    142         /// <summary>
    143         /// 吃(重写父类虚方法)
    144         /// </summary>
    145         public override void Eat()
    146         {
    147             Console.WriteLine("我喜欢吃草!");
    148         }
    149 
    150         /// <summary>
    151         /// 叫(重写父类方法)
    152         /// </summary>
    153         public override void Shout()
    154         {
    155             Console.WriteLine("咩!咩!咩!");
    156         }
    157     }
    //调用方法
    Animal dog = new Dog("旺财");
    string myName=dog.MyName;
    Console.WriteLine(myName);
    dog.Eat();
    dog.Shout();
    //运行结果如下:
    我是:狗狗,我叫:旺财
    我喜欢吃肉!
    汪!汪!汪!
    //调用方法
    Animal sheep = new  Sheep("美羊羊");
    string myName = sheep.MyName;
    Console.WriteLine(myName);
    sheep.Eat();
    sheep.Shout();
    //运行结果如下:
    我是:羊羊,我叫:美羊羊
    我喜欢吃草!
    咩!咩!咩!

    重写(new)

    new:覆盖指的是不同类中(基类或派生类)有两个或多个返回类型、方法名、参数都相同,但是方法体不同的方法。但是这种覆盖是一种表面上的覆盖,所以也叫隐藏,被覆盖的父类方法是可以调用得到的。

    下面用实例说明,代码如下:

     1     /// <summary>
     2     /// 动物类(父类)
     3     /// </summary>
     4     class Animal
     5     {
     6        /// <summary>
     7        /// 名字
     8        /// 说明:类和子类可访问
     9        /// </summary>
    10        protected string name;
    11 
    12 
    13         /// <summary>
    14         /// 构造函数
    15         /// </summary>
    16         /// <param name="name"></param>
    17         public Animal(string name)
    18         {
    19             this.name=name;
    20         }
    21 
    22         /// <summary>
    23         /// 名字(虚属性)
    24         /// </summary>
    25         public virtual string MyName
    26         {
    27             get { return this.name; }           
    28 
    29         }
    30 
    31         /// <summary>
    32         /// 吃(虚方法)
    33         /// </summary>
    34         public virtual void Eat()
    35         {
    36             Console.WriteLine("我会吃!");
    37         }
    38 
    39         /// <summary>
    40         /// 叫(虚方法)
    41         /// </summary>
    42         public virtual void Shout()
    43         {
    44             Console.WriteLine("我会叫!");
    45         }
    46     }
    47 
    48     /// <summary>
    49     /// 狗(子类)
    50     /// </summary>
    51     class Dog:Animal
    52     {
    53         string myName;
    54         public Dog(string name): base(name)
    55         {
    56             myName = name;
    57         }        
    58         /// <summary>
    59         /// 名字(重写父类属性)
    60         /// </summary>
    61         public override string MyName
    62         {
    63             get { return "我是:狗狗,我叫:"+this.name; }
    64         }
    65 
    66         /// <summary>
    67         /// 吃(重写父类虚方法)
    68         /// </summary>
    69         new  public   void Eat()
    70         {
    71             Console.WriteLine("我喜欢吃肉!");
    72         }
    73 
    74         /// <summary>
    75         /// 叫(重写父类方法)
    76         /// </summary>
    77         public new void Shout()
    78         {
    79             Console.WriteLine("汪!汪!汪!");
    80         }
    81     }
    //调用方法
    Animal dog = new Dog("旺财");
    string myName=dog.MyName;
    Console.WriteLine(myName);
    dog.Eat();
    dog.Shout();
    //执行结果如下:
    我是:狗狗,我叫:旺财
    我会吃!
    我会叫!

    如下改一下调用方法:

    //调用方法
    Dog dog = new Dog("旺财");
    string myName=dog.MyName;
    Console.WriteLine(myName);
    dog.Eat();
    dog.Shout();
    //执行结果如下:
    我是:狗狗,我叫:旺财!
    我爱吃肉!
    汪!汪!汪!

    可以看出,当派生类Dog的Eat()方法使用new修饰时,Dog的对象转换为Animal对象后,调用的是Animal类中的Eat()方法。其实可以理解为,使用new关键字后,使得Dog中的Eat()方法和Animal中的Eat()方法成为毫不相关的两个方法,只是它们的名字碰巧相同而已。所以, Animal类中的Eat()方法不管用还是不用virtual修饰,也不管访问权限如何,或者是没有,都不会对Dog的Eat()方法产生什么影响(只是因为使用了new关键字,如果Dog类没用从Animal类继承Eat()方法,编译器会输出警告)。 

    我想这是设计者有意这么设计的,因为有时候我们就是要达到这种效果。严格的说,不能说通过使用new来实现多态,只能说在某些特定的时候碰巧实现了多态的效果。

    3.3 要点:

    a.多态是面向对象的重要特性之一,指同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。

    b.多态分为两种:一种是编译时多态,使用重载实现;另一种是运行时多态,使用重写实现;

    c.重写有两种,一种使用override关键词,另一种使用new关键词

    d.new重写实际上是对父类方法的隐藏,被覆盖的父类方法可以调用得到。因此new可以重写(或说是隐藏)的父类方法不一定要定义为虚方法或抽象方法。只是如果父类方法是虚方法或抽象方法时会覆盖父类方法,如果不是,则隐藏。

    e.重载和覆盖的发生条件:
    重载,必然发生在一个类中,函数名相同,参数类型或者顺序不同构成重载,与返回类型无关
    重写,必然发生在基类和派生类中,其类函数用virtual修饰,派生类用override修饰
    覆盖,在子类中写一个和基类一样名字(参数不同也算)的非虚函数,会让基类中的函数被隐藏,编译后会提示要求使用New关键字 new 修饰 

    隐藏,在子类中可以通过new 隐藏父类的方法

    f.new覆盖与重写、重载的区别:

    当子类与父类的参数不同时

    当基类函数不是虚函数时,基类函数将被隐藏。(因为子类和基类不在同一范围内,所以不是重载)

    当基类函数是虚函数时,基类函数将被隐藏。(因为子类和基类不在同一范围内,所以不是重载;因为参数不同,所以不是重写)

    当子类与父类的参数相同时

    当基类函数不是虚函数时,基类函数将被隐藏。(因为子类和基类不在同一范围内,所以不是重载,因为基类不是虚函数,所以是隐藏不是重写)

    当基类函数是虚函数时,基类函数将被覆盖。(因为子类和基类不在同一范围内,所以不是重载)

    ==============================================================================================

    返回目录

     <如果对你有帮助,记得点一下推荐哦,有不明白的地方或写的不对的地方,请多交流>

    ============================================================================================== 

  • 相关阅读:
    基础算法:求目标值 &字符串反转
    算法计算出股票最佳交易时间点
    Python 设计模式—命令模式
    Python 设计模式—代理模式
    有趣的算法题~单调栈
    令人头大的字符串—算法处理
    WebUI 自动化测试的经典设计模式:PO
    双指针—滑动窗口算法解析
    Python 设计模式—观察者模式
    多线程之读写锁原理
  • 原文地址:https://www.cnblogs.com/yubinfeng/p/4557160.html
Copyright © 2011-2022 走看看