zoukankan      html  css  js  c++  java
  • 细说C#继承

    简介

    继承(封装、多态)是面向对象编程三大特性之一,继承的思想就是摈弃代码的冗余,实现更好的重用性。

    继承从字面上理解,无外乎让人想到某人继承某人的某些东西,一个给一个拿。这个语义在生活中,就像

    家族继承财产,爷爷将财产继承给儿女,儿女在将财产继承给子孙,有些东西可以继承有些的东西只继承给

    某人。映射到编程当中,其思想也大致如此。


     通过示例引出继承的作用

    在代码中定义个三个类:Cat猫、Dog狗、Cattle牛。

    从类图上可以看出红色标识区域,三个类的定义出现了大量的冗余(字段、属性、方法),那么在编写代码时就会出现大量的重复代码。

    试想一下,随着业务功能的扩展,可能会出现更多类,那么冗余(重复的代码)会更多。比如出现同样会造成冗余的类:

    Pig猪、Panda熊猫、Sheep羊......等等。这些类同样会有相同的特征:名称、性别、年龄、奔跑(字段、属性、方法)。


     如何解决此类冗余问题 —— 使用继承

    继承的思想:

    当我们定义了多个类,这多个类都存在重复的成员(共性)。我们可以将这些重复的成员单独的提取封装到一个类中,作为这些具有相同特征类的父类。

    将此思想作用于上述的三个类

    提取共性:可以直观看出重复的具有共性的项目有:1.字段和属性(年龄、姓名、性别)、2.方法(奔跑)。

    封装到一个类:如何定义这个类?Cat猫、Dog狗、Cattle牛有明显共同的特性,就是他们都是动物,故可以抽象定义一个Animal动物类。

    如何在代码中实现继承

        class Animal
        {
            private string name;
            public string Name
            {
                get { return name; }
                set { name = value; }
            }
    
            private string gender;
            public string Gender
            {
                get { return gender; }
                set { gender = value; }
            }
            
            private int age;
            public int Age
            {
                get { return age; }
                set { age = value; }
            }
            
            public void Run()
            {
                Console.WriteLine("奔跑。。。");
            }
    
        }
    
        class Cat:Animal
        {   
            public void CatchMouse()
            {
                Console.WriteLine("抓老鼠。。。");
            }
        }
    
        class Dog:Animal
        {
            public void GuardHouse()
            {
                Console.WriteLine("看家护院。。。");
            }
        }
    
        class Cattle:Animal
        {
            public void Plowland()
            {
                Console.WriteLine("耕田。。。");
            }
        }

    通过一个简单的  :(冒号)实现了继承关系。

    实现继承后产生了两个角色:1.子类(派生类)、2.父类(基类)

    代码中子类删除父类提取的重复性成员。

    实现继承后的关系如下图:

    实现继承后每个子类仅保留了自己特有的特性,大大减少了冗余。

    继承后的能力

    子类的共性成员都被父类提取了,那么子类要使用怎么办?

    子类继承父类后,将会隐式继承父类的所有成员,但不包括构造函数。

    在继承后,访问其父类成员,会受到访问修饰符的限制。故,修饰为private的私有成员不会访问到。

    继承的特性

    1.继承的单根性:

      一个子类只能有一个父类,就好比一个人只有一个父亲。

    2.继承的传递性:  

       例如, ClassC 派生自 ClassB,并且 ClassB 派生自 ClassA,则 ClassC 会继承在 ClassB 和 ClassA 中声明的成员。

    依次顺序可以不断向上取。

    图例:


     继承被后的秘密 —— 子类和父类的构造函数(难点)

    给父类编写了一个构造函数,示例代码如下:

     1     class Animal
     2     {
     3         public Animal(string name,string gender,int age)
     4         {
     5             this.Name = name;
     6             this.Gender = gender;
     7             this.Age = age;
     8         }
     9 
    10         private string name;
    11         public string Name
    12         {
    13             get { return name; }
    14             set { name = value; }
    15         }
    16 
    17         private string gender;
    18         public string Gender
    19         {
    20             get { return gender; }
    21             set { gender = value; }
    22         }
    23         
    24         private int age;
    25         public int Age
    26         {
    27             get { return age; }
    28             set { age = value; }
    29         }
    30         
    31         public void Run()
    32         {
    33             Console.WriteLine("奔跑。。。");
    34         }
    35 
    36         private void ri()
    37         { }
    38 
    39     }
    40 
    41     class Cat:Animal
    42     {   
    43         public void CatchMouse()
    44         {
    45             Console.WriteLine("抓老鼠。。。");
    46         }
    47     }
    48 
    49     class Dog:Animal
    50     {
    51         public void GuardHouse()
    52         {
    53             Console.WriteLine("看家护院。。。");
    54         }
    55     }
    56 
    57     class Cattle:Animal
    58     {
    59         public void Plowland()
    60         {
    61             Console.WriteLine("耕田。。。");
    62         }
    63     }

    尝试运行:

    为什么会提示报这个错误?意思说父类不能没有一个无参的构造函数。

    学过构造函数的应该都会知道,类在没有指定任何构造函数的情况下,程序默认会指派一个无参的构造函数。

    上述的例子由于我们手动添加的那个构造函数,默认的构造函数就被清除掉了。

    在暂且不知道原因的情况下,我们尝试补全那个无参的构造函数,在进行生成代码,此时编译通过没有报错。

    根据此特征我们可以推测子类和父类的构造函数一定有关系,但一定不是继承关系

     

    尝试调用刚刚定义的父类无参构造函数,在调用列表并没有显示,只显示了类自身的一个无参构造函数。

    证明了子类不能继承父类的构造函数。

    通过调试代码监视子类实例化对象的过程,看它到底和父类的构造函数发生了什么。

    通过调试发现在创建子类对象时的代码执行逻辑如下:

    子类会首先去默认执行父类的无参构造函数,然后在执行自己的构造函数

    这条定论就很好的解释了,为什么在上述例子为什么会出现的错误。但是子类又为什么要先去执行父类的构造函数?

    解释:

    因为子类继承了父类的成员,这一项描述只能说明子类拥有的权利,并不代表子类去执行了。

    在原则上要使用类的成员,必须要通过类的实例对象去调用。所以子类要调用到父类的成员,就必须去通过调用

    父类的构造函数,在子类的内部创建一个父类的对象,以便自己去调用父类的成员。

    总结:

    子类始终要使用父类的一个构造函数在自己内部创建一个父类对象,为了调用父类的成员。

    子类默认调用父类的无参构造函数,所以在显示编写一个有参构造函数时导致父类没有了无参构造函数,从而编译出错。


     在子类中使用显示调用父类构造函数

     

    作用1:

    提高代码重用性,子类无需在类中定义,直接使用父类的。

    作用2:

    上述例子讲过子类在实例化对象时会调用父类的默认无参构造函数,因为子类的目的就是通过父类构造函数创建一个对象。

    通过这样显示的调用,那么在父类有没有无参构造函数都没什么关系了。


    子类中存在和父类中相同的成员

    示例:

    根据VS给我们提示的消息,我们可以看出,当代码中存在子类的成员和父类的成员相同的时候,子类的成员将父类的成员隐藏了。

    隐藏过后子类将无法访问到父类的成员。如果是刻意为之,我们可以使用new 关键字显示的说明,从而提高可读性。

    指定new关键字:

     

    此时提示的波浪线已消除。


     其他注意点

    在C#中,所有的类都直接或间接的继承自object类(当我们定义一个类的时候,如果没有给该类指定继承一个类,那么这个类就继承了object类)。

  • 相关阅读:
    Android服务之bindService源代码分析
    [iOS 高级] iOS远程推送与本地推送大致流程
    redis集群
    面试你之前,我希望在简历上看到这些!
    解决安卓出现导入第三方包反复的错误
    【PA2012】【BZOJ4289】Tax
    Spark jdbc postgresql数据库连接和写入操作源代码解读
    Java中hashcode的理解
    CvArr、Mat、CvMat、IplImage、BYTE转换
    CSDN日报20170413 ——《天天写业务代码的那些年,我们是怎样成长过来的》
  • 原文地址:https://www.cnblogs.com/green-jcx/p/9401608.html
Copyright © 2011-2022 走看看