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()方法改写成抽象方法,来重构代码
- 当我们将virtual关键字 改成 abstract关键字时,编译器提示了两个错误:
- 根据错误提示,我们将基类代码改写成如下形式:
abstract class Shape
{
public double Area { get; set; }
public string Color { get; set; }
//打印属性
public void ShowInfo()
{
Console.WriteLine($"面积:{Area},颜色:{Color}");
}
//打印特征
public abstract void ShowFeature();
}
- 至此我们就完美解决了:普通类型作为基类,ShowFeature(){}方法中虽提供了方法体,但却不想提供实现的问题。(完结撒花)。。。别急再往下看一下抽象类的定义吧。
引入抽象类:专为被用作其他类的基类而设计的类。
- 抽象类不能被实例化:抽象类中由于含有没有实现的函数,如果抽象类能够被实例化,那么当抽象类的引用调用到这个没有实体的函数时,计算机就不知道如何执行这个函数的行为,这样会导致程序崩溃;所以干脆编译器不让抽象类被实例化--设定抽象类被实例化的代码就会编译报错
- 抽象类可以有抽象成员、也可以有普通的非抽象成员:从上面的代码示例就可以验证这点--Shape类既有普通的属性Area、Color;普通的方法ShowInfo();还可以有抽象方法ShowFeature()
- 任何派生自抽象类的子类必须使用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();--这是由抽象类完成多态,相比上一篇的普通类的多态形式是不是更好一些?
抽象类中的成员可以都是抽象成员,不含一个非抽象成员--当然如果真的是需要这样实现的话,有专门的类型--接口来实现;以上便是对抽象类的总结,记录下来以便以后查阅。