本文主要介绍抽象类及其必要性。
1. 一个继承实例
在引入抽象类之前,先看这样一段代码
using System;
namespace AbstractClassLearning
{
class Program
{
static void Main(string[] args)
{
Vehicle vehicle1 = new Car();
Vehicle vehicle2 = new Truck();
vehicle1.Stop();
vehicle2.Stop();
}
}
class Vehicle
{
public void Run()
{
Console.WriteLine("Running!");
}
public void Stop()
{
Console.WriteLine("The vehicle stops.");
}
}
class Car : Vehicle
{
public void Stop()
{
Console.WriteLine("The car stops.");
}
}
class Truck : Vehicle
{
public void Stop()
{
Console.WriteLine("The truck stops.");
}
}
}
运行结果
代码分析
Car 类 和 Truck 类继承了 Vehicle 类,并且想通过多态技术在控制台打印出正确的 Stop() 方法,显然上述代码并未实现。可通过以下两种方式实现。
① 可通过重写实现,加入关键字 virtual 和 override。
using System;
namespace AbstractClassLearning
{
class Program
{
static void Main(string[] args)
{
Vehicle vehicle1 = new Car();
Vehicle vehicle2 = new Truck();
vehicle1.Stop();
vehicle2.Stop();
}
}
class Vehicle
{
public void Run()
{
Console.WriteLine("Running!");
}
public virtual void Stop()
{
Console.WriteLine("The vehicle stops.");
}
}
class Car : Vehicle
{
public override void Stop()
{
Console.WriteLine("The car stops.");
}
}
class Truck : Vehicle
{
public override void Stop()
{
Console.WriteLine("The truck stops.");
}
}
}
运行结果
② 可通过改变基类里的逻辑来实现,判断实例对象的类型。
using System;
namespace AbstractClassLearning
{
class Program
{
static void Main(string[] args)
{
Vehicle vehicle1 = new Car();
Vehicle vehicle2 = new Truck();
vehicle1.Stop();
vehicle2.Stop();
}
}
class Vehicle
{
public void Run()
{
Console.WriteLine("Running!");
}
// 改变基类逻辑,判断实例对象类型
public void Stop()
{
if(this is Car)
{
Console.WriteLine("The car stops.");
}
else if (this is Truck)
{
Console.WriteLine("The truck stops.");
}
}
}
class Car : Vehicle
{
public void Stop()
{
Console.WriteLine("The car stops.");
}
}
class Truck : Vehicle
{
public void Stop()
{
Console.WriteLine("The truck stops.");
}
}
}
运行结果
代码分析
① 利用重写时,基类的 Stop() 方法体没有必要写出,因为也不会执行;
② 改变基类 Stop() 方法的逻辑时,不符合开闭原则。因为基类一旦构建完成,不希望再次对其更改。
因此,上述两种方式虽然都能实现想要的效果,但均有不足之处,此时引入抽象类。
2. 抽象类
① 抽象类可有抽象成员和具体成员,抽象成员即未被实现逻辑。
② 抽象成员不能为 private,不能有任何逻辑
2.1 更改代码
利用抽象类更改后的代码如下
using System;
namespace AbstractClassLearning
{
class Program
{
static void Main(string[] args)
{
Vehicle vehicle1 = new Car();
Vehicle vehicle2 = new Truck();
vehicle1.Stop();
vehicle2.Stop();
}
}
abstract class Vehicle
{
public void Run()
{
Console.WriteLine("Running!");
}
abstract public void Stop();
}
class Car : Vehicle
{
public override void Stop()
{
Console.WriteLine("The car stops.");
}
}
class Truck : Vehicle
{
public override void Stop()
{
Console.WriteLine("The truck stops.");
}
}
}
运行结果
上述代码,基类 Vehicle 变为抽象类,包括一个抽象方法 Stop() 和一个具体方法 Run()。如果再有其他类继承 Vehicle 时,只需实现 Stop() 方法即可,用关键字 Override。
2.2 抽象类总结
- 抽象类的作用有两个:第一,可作为基类;第二,可以声明变量,引用其子类的一个实例对象,实现多态。
- 抽象类不能实例化;
- 抽象类的抽象成员必须被其子类实现,使用关键字 abstract 和 override;
- 开闭原则:封闭固定及确定了的成员,不确定的定义为抽象成员
- 如果抽象类中只有抽象成员,则为接口 Iterface。
3. 接口
3.1 更改代码
using System;
namespace AbstractClassLearning
{
class Program
{
static void Main(string[] args)
{
IVehicle vehicle1 = new Car();
IVehicle vehicle2 = new Truck();
vehicle1.Stop();
vehicle2.Stop();
}
}
interface IVehicle
{
void Stop();
}
class Car : IVehicle
{
public void Stop()
{
Console.WriteLine("The car stops.");
}
}
class Truck : IVehicle
{
public void Stop()
{
Console.WriteLine("The truck stops.");
}
}
}
运行结果
3.2 接口小结
- 接口的所有成员均为抽象的,且默认为 public,因此接口内的成员不用也不能写 abstract public;
- 接口用关键字 interface,且接口命名规格为IVehicle,I 代表接口;
- 接口也不能实例化;
- 接口可以多继承,与类不同。