C#:类的继承

其实我们对继承并不陌生:


观察上述代码,我们可以看到:在我们并未给Program类声明任何成员时,Program实例却可以通过"."操作符呼出调用列表?这是因为:我们声明的类默认继承了Object类型,它是.NET所有数据类型的基类型
如何证明Program类型的基类型是Object?

第一种方式:我们可以在ToString()方法上右键,选择"转到定义"或者直接在ToString()方法上按下"F12"键


观察上述代码,我们可以看到:ToString()方法,确实定义在了Object类中;由于Program类默认继承了Object类,所以Program实例也就拥有了ToString()这个公开的函数成员。

第二种方式:我们可以通过typeof操作符来获取Program的基类型

class Program
{
    static void Main(string[] args)
    {
        Type type = typeof(Program);
        var baseType = type.BaseType;
        Console.WriteLine(baseType.FullName);
        Console.ReadLine();
    }
}
/*输出:System.Object*/

观察上述代码,我们也可以得出Program的基类型是Object

什么是继承?

我想单单通过对继承一通描述,并不能直观的感受继承是什么东西,下面我们通过代码示例来认识继承。

1) 我们需要定义一个长方形和圆形,并且通过ShowInfo方法获取他们的面积和颜色。在不使用继承的情况下我们会这样实现:

namespace ExtendsDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            Square square = new Square(32.3,"红色");
            Circle circle = new Circle(33.4,"蓝色");
            square.ShowInfo();
            circle.ShowInfo();
            Console.ReadLine();
        }
    }
}
//先声明一个 长方形类
class Square
{
    public double SquareArea { get; set; }
    public string SquareColor { get; set; }
    public Square(double area,string color)
    {
        this.SquareArea = area;
        this.SquareColor = color;
    }
    public void ShowInfo()
    {
        Console.WriteLine($"面积:{this.SquareArea},颜色:{this.SquareColor}");
    }
}
//然后再声明一个 
class Circle
{
    public double CircleArea { get; set; }
    public string CircleColor { get; set; }
    public Circle(double area, string color)
    {
        this.CircleArea = area;
        this.CircleColor = color;
    }
    public void ShowInfo()
    {
        Console.WriteLine($"面积:{this.CircleArea},颜色:{this.CircleColor}");
    }
}
/* 输出:
面积:32.3,颜色:红色
面积:33.4,颜色:蓝色
*/

观察上面代码,我们可以看到:Square和Circle类的相似度很高,假如说我们此时还需要一个三角形并需要得知它的面积和颜色,那么我们可能就需要"Copy"、"Paste"代码来实现了。而这种解决方式肯定是不可取的。

2) 当我们面对相似度很高的代码片段时,第一反应就是要使用继承:通过定义一个基类型将相似的代码片段集中放置到基类型中以达到代码复用的目的。

namespace ExtendsDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            Square square = new Square(32.3,"红色");
            Circle circle = new Circle(33.4,"蓝色");
            square.ShowInfo();
            circle.ShowInfo();
            Console.ReadLine();
        }
    }
}

class Shape
{
    public double Area { get; set; }
    public string Color { get; set; }
    public Shape(double area)
    {
        
    }
    public Shape(double area,string color)
    {
        this.Area = area;
        this.Color = color;
    }
    public void ShowInfo()
    {
        Console.WriteLine($"面积:{this.Area},颜色:{this.Color}");
    }
}
//在声明类型的后面添加":"和类型(作为基类型),便完成了类的继承
class Square : Shape
{
    public Square(double area, string color) : base(area)
    {

    }
}

class Circle : Shape
{
    public Circle(double area,string color):base(area,color)
    {

    }
}

我们通过改写代码,定义了一个Shape类型作为基类型,然后让Squre和Cirlcle继承Shape类,便完成了对代码的重构,当我们需要一个Trangle形状时,便可以在声明类的时候继承Shape类,快速应对需求的变动。

由以上面的代码为例,我们来看以下问题:

1) 当子类继承父类时,子类究竟从父类中继承到了什么?

子类继承了父类的数据成员和函数成员(构造函数除外)--这一点从上述Square和Circle类中除构造函数外没有写声明任何成员,却能调用ShowInfo()方法可以得知。

2) 当子类实例化时,为什么父类构造函数也会调用?

  • 当子类实例化时,若父类没有显示提供任何构造函数,那么子类会默认隐式调用父类的默认构造函数:也就是说父类构造函数会先调用。
  • 若父类没有默认的无参构造函数,C#编译器不会帮我们隐式调用基类无参构造函数了;但是我们还必须得调用基类的构造函数,那么子类就需要使用base上下文关键字来指定调用基类中的哪一个构造函数。
  • 到这里,我们可以引用微软官方文档中对base的定义:base关键字用于从派生类中访问基类成员
  • 从上面两点可以看出,子类并不能继承父类的构造函数;换言之构造函数不能被继承。--如果说构造函数能够被继承,那么何必费心在子类中调用基类构造函数呢。

3)创建子类实例时,有没有创建基类实例?

  • 首先答案是:没有;我们只说过当子类型实例创建时,会先调用父类的构造函数;并没有说会创建父类实例;
  • 其次,我们可以在父类构造函数中和子类构造函数中打印一下HasCode,从打印结果证明new一个子类对象,并不会创建基类对象。
namespace ExtendsDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            Square square = new Square(32.3, "红色");
            square.ShowInfo();
            Console.ReadLine();
        }
    }
}
//先声明一个 长方形类
class Shape
{
    public double Area { get; set; }
    public string Color { get; set; }
    public Shape(double area, string color)
    {
        this.Area = area;
        this.Color = color;
        Console.WriteLine(this.GetHashCode());
    }


    public void ShowInfo()
    {
        Console.WriteLine($"面积:{this.Area},颜色:{this.Color}");
    }
}

class Square : Shape
{
    public Square(double area, string color) : base(area, color)
    {
        Console.WriteLine(base.GetHashCode() + "---" + this.GetHashCode());
    }
}
/* 输出:
46104728
46104728---46104728
面积:32.3,颜色:红色
*/

4) base就是充当一个调用基类成员的角色;它不能单独输出。

5)下面的代码中base.Area和this.Area的base和this灰掉,说明了在当前情况下使用base和this没有区别:他们都指向的内存中同一个地方。


那什么情况下base和this才有区别呢?这个在讲多态的时候再介绍。

前面所讲的例子,都是在帮助我们理解类的继承的一些基本概念,其实类的继承还有很多应用没有讲到,比如说:在子类中添加父类没有的成员,这一操作我们称为类成员的横向扩展;

class Shape
{
    public double Area { get; set; }
    public string Color { get; set; }
    public Shape(double area, string color)
    {
        this.Area = area;
        this.Color = color;
        Console.WriteLine(this.GetHashCode());
    }

    public void ShowInfo()
    {
        Console.WriteLine($"面积:{this.Area},颜色:{this.Color}");
    }
}

class Square : Shape
{
    //子类成员越来越多了,相对于基类看起来像是长胖了--横向扩展
    public string Foreground { get; set; }
    public Square(double area, string color) : base(area, color)
    {
        Console.WriteLine(base.Area + "---" + this.Area);
    }
}

以上便是对类的继承的简要总结,记录下来以便以后查阅。

posted @ 2020-10-25 22:19  BigBosscyb  阅读(898)  评论(0编辑  收藏  举报