抽象类部分
先了解一下概念
什么是抽象类?什么是接口?
1. 接口和抽象类都是“软件工程产物”
2. 具体类-》抽象类-》接口:越来越抽象,内部实现的东西越来越少
3. 抽象类是未完全实现逻辑的类(可以有字段和非public成员,他们代表了“具体逻辑”)
4. 抽象类为复用而生:专门作为类的基础来使用,也具有解耦功能
5. 封装确定的,开放不确定的,推迟到合适的子类中去实现
6. 接口是完全未实现逻辑的“类”(“纯虚类”;只有函数成员;成员全部public)
7. 接口为解耦而生:高内聚,低耦合,方便单元测试
8. 接口是一个“协约”,早已为工业生产所熟知(有分工必有协作,有协作必有协约)
9. 它们都不能实例化,只能用来声明变量,引用具体类(concrete class)
再了解一下开闭原则
1. 如果不是为了修BUG或者添加新的功能的话,闲着没事别乱修改类的代码,特别是这个类中函数成员的代码
2. 我们应该封装那些不变的,稳定的,固定的和确定的成员;而那些不确定的,有可能改变的成员应该声明为抽象成员并且留给子类去实现
具体一点,这个东西就是抽象类:
abstract class Vehicle
{
public void Stop()
{
Console.WriteLine("Stopped!");
}
abstract public void Run();
}
1. 类前要用abstract修饰
2. 抽象类表示函数成员没有被完全实现的类,即这个类里可以有若干个函数成员,但是至少有一个是未实现的
3. 没有被实现的这个成员用abstract修饰,但是不能用private修饰,因为最后实现这个抽象类的一定是子类,但是子类没法访问private
4. 成员一旦被abstract修饰,是不能有任何逻辑实现的
5. 因为抽象类里有未被实现的抽象成员,没有具体行为,所以没法实例化
6. 不能实例化,抽象类的作用就只剩两个了,一是作为基类让别人继承,在子类实现这些没有实现的成员;二是去声明变量,用这种基类类型的变量去引用自己的子类实例,这个子类已经实现了抽象类中一些未实现的方法,这是可以的
来看一个例子,注意请按照注释顺序查看:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _12.抽象类
{
internal class Program
{
static void Main(string[] args)
{
//1.4 但是这样会出现一个新问题 往下看1.41
//Vehicle vehicle = new Car();
//vehicle. 点不出来Run方法,只有Stop,因为没在基类定义Run
//1.6 此时vehicle.候选词既能看到Run也能看到Stop 往下看1.7
//vehicle.Run();
//2. 抽象类唯一能做的事就是给别的类当基类,以基类类型声明变量 ,并且引用那些已经完全实现了他那些抽象成员的子类实例来玩多态
Vehicle v = new RaceCar();
v.Run();
Console.ReadLine();
}
}
//1.3 所以我创建一个基类,并且把Car和Truck的Stop删除 往上看1.4
abstract class Vehicle//1.72 类也要变成抽象类,此时也符合开闭原则 往下看1.73
{
public void Stop()
{
Console.WriteLine("Stopped!");
}
public void Fill()
{
Console.WriteLine("Pay and fill...");
}
//1.41 来看一个解决方法,但是这个方法违反开闭原则 往下看1.5
//public void Run(string type)
//{
// if (type=="car")
// {
// Console.WriteLine("Car is running......");
// }
// else if (type == "truck")
// {
// Console.WriteLine("Truck is running......");
// }
//}
//我们可以这样解决当前的问题,但是如果我又买了一辆车呢,就会频繁更改这个类
//1.5 使用虚方法的重写解决这个问题 往下看1.51
public abstract void Run();//1.71 此时就可以改成抽象成员 往上看1.72
//{
// Console.WriteLine("Vehicle is running...");//1.7 此时发现这行根本没人用 往上看1.71
//}
}
//1.0 先创建一个小汽车,车辆有启动和停止两种方法,往下看1.1
class Car : Vehicle
{
//1.51 这里要加override 往下看1.52
public override void Run()
{
Console.WriteLine("Car is running......");
}
//public void Stop()
//{
// Console.WriteLine("Stopped!");
//}
}
//1.1 我又买了个卡车,卡车也有上面两个方法,所以我复制下来改个名字 往下看1.2
//1.2 此时他们有相同的停止方法Stop,而且我也违反了不能Copy/Pasted的原则 往上看1.3
class Truck : Vehicle
{
//1.52 这里同上 往上看1.6
public override void Run()
{
Console.WriteLine("Truck is running......");
}
//public void Stop()
//{
// Console.WriteLine("Stopped!");
//}
}
//1.73 比如我又买了一辆赛车,我在下面添加一辆赛车但是不需要在Vehicle增加删除成员 往下看1.74
class RaceCar : Vehicle//1.74 此时报警问你是不是要实现抽象类,alt+enter选择“实现抽象类” 往下看1.75
{
public override void Run()//1.75 然后会自动生成这个方法 往上看2
{
//throw new NotImplementedException();这行不要
Console.WriteLine("RaceCar is runnning...");
}
}
}
抽象类和接口的关系
看完上面的例子我们知道抽象类是具有一个或多个抽象成员的类,已知最少需要一个抽象成员,那最多能有几个呢?抽象类中可否全都是抽象成员?答案是可以!
当类中所有成员都是抽象成员的时候
请同样按照注释顺序阅读
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _12.抽象类
{
internal class Program
{
static void Main(string[] args)
{
Vehicle v = new RaceCar();
v.Run();
Console.ReadLine();
}
}
//1. 创建一个全抽象的类,纯虚类 往下看1.1
//2. 在C#里面,这种纯抽象类,纯虚类实际上就是接口 往下看2.1
//abstract class VehicleBase
//{
// abstract public void Stop();
// abstract public void Fill();
// abstract public void Run();
//}
//2.1 来看看接口怎么定义,先把修饰改成interface 往下看2.2
interface IVehicle //VehicleBase 3. 接口的命名一般是I开头,这里将VehicleBase 替换为IVehicle 往下看3.1
{
//2.2 接口默认为纯虚公共方法,所以abstract public都可以删除 往下看2.3
void Stop();
void Fill();
void Run();
}
//1.1 把Stop和Fill扔给了vehicle 往下看1.2
abstract class Vehicle : IVehicle //VehicleBase 3.1 同上
{
//2.3 override要去掉,此时就变成了我们的类去实现接口 往下看2.4
public void Stop()
{
Console.WriteLine("Stopped!");
}
public void Fill()
{
Console.WriteLine("Pay and fill...");
}
// public abstract void Run();//这行可以不要,已经在上面定义了
//2.4 此时继承类Vehicle:VehicleBase会报警,因为没有实现接口成员Run,只实现了另外两个,此时可以保留一个abstract下推给其他派生类 往上看3
abstract public void Run();
}
//1.2 Run继续往下扔给Car他们 往上看2
class Car : Vehicle
{
public override void Run()
{
Console.WriteLine("Car is running......");
}
}
class Truck : Vehicle
{
public override void Run()
{
Console.WriteLine("Truck is running......");
}
}
class RaceCar : Vehicle
{
public override void Run()
{
Console.WriteLine("RaceCar is runnning...");
}
}
}
通过上面的例子我们知道在C#里面,纯抽象类或纯虚类实际上就是接口
在本例的继承体系当中,我们把纯虚类替换成了接口,接口下推一级我们用一个抽象类Vehicle做了一个不完全的实现,然后用这个不完全实现的抽象类作为基类,再派生出其他的具体类Car,Truck和RaceCar