• 关于C#中的抽象类、抽象方法和虚方法的探究


         2016年的第一篇文章,容我先喷喷新年的情怀,..........,好了,喷的差不多了。  

         在面向对象中,我们需要对类进行横向和纵向的认识,不同的类有不同特色的成员,同时在不同类的继承中,子类获得父类的成员也是丰富多彩。

    先概述:

             1.抽象类

                抽象类中不是所有的方法都是抽象方法,对于单纯抽象类而言,是限制类的实例化,可以没有抽象方法。甚至没有任何成员也没事。

             2.抽象方法

                抽象方法的目的是为了让派生类实现其方法,抽象的方法不实现,抽象方法没有方法体,没有大括号。包含抽象方法的类一定是抽象类,所以含有抽象方法(属性)是这个类是抽象类的充分非必要条件。

             3.虚方法

                虚方法是一种方法,不是抽象的,它有大括号,是实现的,子类可以重写,可以覆盖。虚方法是父类中不一定继承到子类中的,具体由子类决定(这里指的是子类可以重写这个方法,按自己子类的逻辑写,不用父类中的方法的逻辑,但是父类中的实方法就没得选了,会继承下来的,这里重点说的是方法内容上的继承),它与抽象方法不同,虚方法与一般的方法相比,可实现由子类选择是否继承。

    上代码:

                1.生命类   life

          

     
     1 public abstract class life   //抽象类
     2     { 
     3         public string name;
     4 
     5         public abstract void live(); 
     6 
     7         public abstract void death();
     8 
     9         public virtual void birth()
    10         { 
    11             Console.WriteLine("life类的birth()虚方法");
    12         }
    13 
    14         public void huozhe()
    15         {
    16             Console.WriteLine("life类的huozhe()方法");
    17         }
    18         public life()
    19         {
    20             Console.WriteLine("life被构造");
    21         }
    22     }
    View Code

               2.动物类  animal        继承 life

     
     1 public class animal :life
     2     {
     3        public animal()
     4        {
     5            Console.WriteLine("animal被构造");
     6        }
     7          public string chi;
     8 
     9        //子类必须实现父类中的所有的抽象方法,即abstract的方法
    10         
    11           public override void live()
    12         {
    13            Console.WriteLine( "animal类必须实现live的live()抽象方法");
    14         }
    15           public override void death()
    16           {
    17               Console.WriteLine("animal类必须实现live的death()抽象方法");
    18           }
    19 
    20           public override  void birth()  //重写父类life中的虚方法用override  //重写后将以实方法看待
    21           {
    22               Console.WriteLine("animal完成对life类中的birth()虚方法的重写");
    23           }
    24 
    25           public new void huozhe()   //覆盖父类life中的huozhe()的方法 实例化时,用该方法
    26           {
    27               Console.WriteLine("animal 活着");
    28           }          
    29     }
    View Code

               3.人类    people   密封类      继承 animal    

     
     1 //覆盖与重写的不同 在形式上
     2     public sealed class people :animal
     3     {
     4         public void shuo()
     5         {
     6             Console.WriteLine("people中的shuo()方法");
     7         }
     8 
     9         public people()
    10         {
    11             Console.WriteLine("people被构造");
    12         }
    13         public new void birth()  //对animal完成life中的birth()虚方法进行重写后的birth()(已变为实方法)进行覆盖
    14         {
    15             Console.WriteLine("people中覆盖animal重写后的birth()虚方法");
    16         }
    17         public override void death() //people同样可以重写animal中的death的life的抽出方法的重新实现
    18         {
    19             Console.WriteLine("people对animal中实现life的death()抽象方法重新实现");
    20         }
    21         public new void huozhe()   //覆盖父类animal中的huozhe()的方法 实例化时,用该方法
    22         {
    23             Console.WriteLine("people 活着");
    24         }
    25     }
    View Code

              4.女人类  woman        无法继承  people

    开始测试

     1 //覆盖不会改变父类的方法   重写会改变父类的方法  当以子类创建父类的时候会体现出来
     2     class Program
     3     {
     4         static void Main(string[] args)
     5         {
     6             people p1 =new people();   life被构造  animal被构造   people被构造
     7           //  life s1 = new life();   作为abstract的抽象类不能被声明
     8             animal liv1 = new animal();     //life被构造  animal被构造
     9             animal p2 = new people();    //父类调用子类对象
    10             liv1.death();  //输出animal 中的
    11             liv1.huozhe();  //输出animal 中的
    12             liv1.live();   //输出animal 中的
    13             liv1.birth();  //输出animal 中的
    14             Console.WriteLine("---------------------");
    15             people p1 = new people();
    16             p1.death();   //输出people 中的
    17             p1.huozhe();  //输出people 中的
    18             p1.live();    //输出继承animal 中的  //由于没有重写
    19             p1.birth();    //输出people 中的
    20             Console.WriteLine("---------------------");
    21             liv1.death();   //输出animal 中的
    22             liv1.huozhe();  //输出animal 中的
    23             liv1.live();   //输出animal 中的
    24             liv1.birth();  //输出animal 中的           
    25             Console.WriteLine("---------------------");
    26             p2.death();//输出people 中的
    27             p2.huozhe();//输出animal 中的
    28             p2.live();//输出animal 中的
    29             p2.birth();//输出animal中的
    30             Console.WriteLine("-----------------------");
    31             Console.WriteLine("---------转换----------");
    32 
    33          
    34            //  people p3 = new animal();  //出错子类引用不能直接引用父类对象,  除非将父类对象的数据类型强制转换成子类
    35            // people p4 = (people)liv1; // 如果你创建实例的时候没有将父类引用到子类对象,是无法转换的
    36             //  p2.shuo();          //错误,引用不了, 父类引用自雷对象时无法子类中的新方法
    37             people p5 = (people)p2;   //这样转后,能调用 子类中的新方法,说明父类引用时没用丢到子类中的新方法,只是不能调用
    38             p5.shuo();
    39             Console.ReadKey();
    40         }
    41     }
    View Code

    测试结果

    好了,先细细琢磨吧!

    其实执行哪个方法,可以按如下规律:

     A  a=new D();    a.Fun1()   会从A开始找,最长找到D为止,如果 Fun1() 是实例方法,或者new修饰的,那么就是执行A下的Fun1(),如果是虚方法,或者 override方法,继续给下找,直到首次找到 Fun1()是new 的,它父类的Fun1()方法这个方法就是要执行的,  比如  B:A    C:B    D:C   ,如果 Fun()1 在A是virtual ,  B中是 override Fun1(),C中是 override Fun1() , 在D 中有new修饰,那么     执行的是C中的Fun1(),   再看如下示例:

     其中:

    Parent s1 = new LitterSun();

    Son s2 = new LitterSun();

    Sun s3 = new LitterSun();

    Parent s4 = new Sun();

    Parent s5 = new Son();

    s1.Show();  //输出的嘻嘻

    s2.Show(); //输出的嘻嘻

    s3.Show(); //输出的嘻嘻

    s4.Show(); //输出的嘻嘻

    s5.Show(); //输出的嘶嘶

     相信 应该对上面结果理解是ok的

    而对于这种

    Parent s0 = new Son();
    s0.Show();

    显然应该 应该是先走的 Parent的Show,内部不走Parent的DoSome,走的是Son的DoSome ,输出的是 嘶嘶

    再看一个 有意思的

    Parent s0 = new Son();
    s0.Show();

    这个应该输出 哼哼 还是 嘻嘻  还是 嘶嘶了, 其实我们发现virtual 也是具体和new一样的隔断作用的, 这样代码是先走的Parent的show(),因为被Son的virtual隔断了,,然后走Dosome()时选择走 Son的DoSome(),最后输出的是 嘶嘶

    同理: 下面这个

    Parent s0 = new Sun();
    s0.Show();  // 走的Son的Show,走的Sun()的DoSome, 最后输出的是  喔喔 嘻嘻

    随便再说一句,如果想起阻段作用的 我们可以使用new进行覆盖变成新实方法,如果还想让子类再继续能重写的话可以再加个 virtual,如下

    另外 类中的属性是没有多态性的,即你在引用上面使用属性时,系统只会去找引用的静态类型中的那个属性,而与它的实际类型无关。
    静态方法也是没有多态性的。

      

  • 相关阅读:
    人月神话阅读笔记
    12周总结
    IOS成长之路-用NSXMLParser实现XML解析
    沟通能力的表达
    IOS中UIScrollView的contentSize、contentOffset和contentInset属性
    Block 的使用时机
    drawAdapter
    drawpoly()函数的用法
    @synchronized(self)
    Swift2
  • 原文地址:https://www.cnblogs.com/wwkk/p/5197414.html
Copyright © 2020-2023  润新知