C#:为做基类而生的抽象类

需求:我们需要一个Square类和Circle类能够打印出面积、颜色等成员信息;并且还希望他们能够打印出自己的特点。

在不使用继承的情况下我们可以写出如下代码:

namespace AbstractClassDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            Square square = new Square();
            square.ShowFeature();
            square.ShowInfo();

            Circle circle = new Circle();
            circle.ShowFeature();
            circle.ShowInfo();

            Console.ReadLine();
        }
    }

    class Square
    {
        public double SquareArea { get; set; }

        public string SquareColor { get; set; }
        //打印属性
        public void ShowInfo()
        {
            Console.WriteLine($"面积:{SquareArea},颜色:{SquareColor}");
        }
        //打印特征
        public void ShowFeature()
        {
            Console.WriteLine("我是长方形,对面平行且相等...");
        }
    }

    class Circle
    {
        public double CircleArea { get; set; }

        public string CircleColor { get; set; }
        //打印属性
        public void ShowInfo()
        {
            Console.WriteLine($"面积:{CircleArea},颜色:{CircleColor}");
        }
        //打印特征
        public void ShowFeature()
        {
            Console.WriteLine("我是圆形,我特圆....");
        }
    }
}
/* 输出:
我是长方形,对面平行且相等...
面积:0,颜色:
我是圆形,我特圆....
面积:0,颜色:
*/

通过观察上面的代码,我们知道,除去ShowFeature方法在两个类中的功能实现略有不同外,其余成员都是相似的,所以根据我们学过的继承的知识,我们可以这样改写代码.

定义基类Shape,将相似的部分声明到基类中,将会产生的变化的函数成员定义成virtual型,供派生类去重写

namespace AbstractClassDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            Shape square = new Square();//赋值号左边使用基类型变量
            square.ShowFeature();
            square.ShowInfo();

            Shape circle = new Circle();//赋值号左边使用基类型变量
            circle.ShowFeature();
            circle.ShowInfo();

            Console.ReadLine();
        }
    }

    class Shape
    {
        public double Area { get; set; }

        public string Color { get; set; }
        //打印属性
        public void ShowInfo()
        {
            Console.WriteLine($"面积:{Area},颜色:{Color}");
        }
        //打印特征
        public virtual void ShowFeature()
        {
            //子类会自己在自己的类中重写的
        }
    }

    class Square : Shape
    {
        public override void ShowFeature()
        {
            Console.WriteLine("我是长方形,对面平行且相等...");
        }
    }

    class Circle : Shape
    {
        public override void ShowFeature()
        {
            Console.WriteLine("我是圆形,我特圆....");
        }
    }
}
/* 输出:
我是长方形,对面平行且相等...
面积:0,颜色:
我是圆形,我特圆....
面积:0,颜色:
*/

到此为止,我们相当于快速回顾了重写和多态;在基类中有一个定义为virtual的函数成员ShowFeature,它并没有提供方法实体,因为我们觉得在这里并没有必要;

基类型中的vitual函数成员不想提供代码实现而是想在子类中通过重写实现,那么我们就可以通过abstract修饰符将其声明为抽象成员:

  • 抽象成员没有方法体;
  • 抽象成员必须用abstract标记且必须是函数成员(函数成员包括:方法、属性、事件、索引器)
  • 抽象成员不能被private修饰且不能出现virtual修饰符;
  • 抽象成员专门为被基类重写而设计的
    所以我们可以将ShowFeature()方法改写成抽象方法,来重构代码
  1. 当我们将virtual关键字 改成 abstract关键字时,编译器提示了两个错误:
  2. 根据错误提示,我们将基类代码改写成如下形式:
abstract class Shape
{
    public double Area { get; set; }

    public string Color { get; set; }
    //打印属性
    public void ShowInfo()
    {
        Console.WriteLine($"面积:{Area},颜色:{Color}");
    }
    //打印特征
    public abstract void ShowFeature();
}
  1. 至此我们就完美解决了:普通类型作为基类,ShowFeature(){}方法中虽提供了方法体,但却不想提供实现的问题。(完结撒花)。。。别急再往下看一下抽象类的定义吧。

引入抽象类:专为被用作其他类的基类而设计的类。

  1. 抽象类不能被实例化抽象类中由于含有没有实现的函数,如果抽象类能够被实例化,那么当抽象类的引用调用到这个没有实体的函数时,计算机就不知道如何执行这个函数的行为,这样会导致程序崩溃;所以干脆编译器不让抽象类被实例化--设定抽象类被实例化的代码就会编译报错
  2. 抽象类可以有抽象成员、也可以有普通的非抽象成员:从上面的代码示例就可以验证这点--Shape类既有普通的属性Area、Color;普通的方法ShowInfo();还可以有抽象方法ShowFeature()
  3. 任何派生自抽象类的子类必须使用override关键字实现该类的所有抽象成员,除非派生类自己也是抽象类
    • 先解释前半句:如果说派生自抽象类的子类不把抽象类中的抽象成员全部重写了,岂不是又犯了:计算机无法执行没有行为的方法的问题?
    • 再解释后半句:后半句可以理解为--抽象类的派生类还是抽象类时,它可以不实现父类的抽象成员;因为这个抽象子类又不能实例化,不用怕计算机执行未提供实际行为的方法的问题;
    • 再说了:如果将来的确需要执行抽象类中定义的那个抽象函数,那么我们终会提供实现了全部抽象成员的子类实例来调用,所以不必心急。

通过下面代码理解抽象类:

namespace AbstractClassDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            //Shape shape = new Shape();//这行代码会报错:无法创建抽象类的实例
            Shape zhengfangxing = new ZhengFangXing();
            zhengfangxing.ShowFeature();
            Console.ReadLine();
        }
    }

    abstract class Shape
    {
        //非抽象的函数成员
        public double Area { get; set; }
        //非抽象的函数成员
        public string Color { get; set; }
        //非抽象的函数成员
        public void ShowInfo()
        {
            Console.WriteLine($"面积:{Area},颜色:{Color}");
        }
        //抽象成员:注意抽象成员不能被private修饰、不能被virtual修饰
        public abstract void ShowFeature();
    }

    abstract class Square : Shape
    {
        //将Square声明成抽象类,可以不必重写Shape类中的抽象方法了
    }

    class ZhengFangXing : Square
    {
        //终于有了一个子类实现了抽象成员--ShowFeature()方法
        public override void ShowFeature()
        {
            Console.WriteLine("我是特殊的长方形,我四条边都相等...");
        }
    }
}
/*输出:我是特殊的长方形,我四条边都相等...*/

分析:Shape zhengfangxing = new ZhengFangXing(); zhengfangxing.ShowFeature();--这是由抽象类完成多态,相比上一篇的普通类的多态形式是不是更好一些?

抽象类中的成员可以都是抽象成员,不含一个非抽象成员--当然如果真的是需要这样实现的话,有专门的类型--接口来实现;以上便是对抽象类的总结,记录下来以便以后查阅。

posted @ 2020-10-27 01:35  BigBosscyb  阅读(569)  评论(0编辑  收藏  举报