概念：
	使用 `abstract` 关键字修饰的类，叫做==抽象类==
	使用 `abstract` 关键字修饰的方法，叫做==抽象方法==

简单理解：
	抽象类像一份“不完整的父类模板”
	它可以规定子类必须有什么能力，但具体怎么做，交给子类自己实现

补充：
	有些教程会把“抽象方法”叫作“抽象函数”，初学阶段可以先理解成同一个意思

通常用法：
1. 多个类有共同身份，但父类本身不应该被直接创建对象
2. 父类能提供一部分公共代码
3. 父类还想强制子类必须实现某些方法
4. 配合[[继承]]和[[多态vob|多态]]，让调用方通过父类统一调用子类行为

----
# 语法
抽象类：
```csharp
abstract class 类名
{

}
```

抽象方法：
```csharp
abstract class 类名
{
	public abstract void 方法名();
}
```

子类实现抽象方法：
```csharp
class 子类名 : 抽象类名
{
	public override void 方法名()
	{

	}
}
```

注意：
	抽象方法没有方法体，结尾直接写分号 `;`

----
# 基础案例
```csharp
abstract class Animal
{
	public string Name;

	public Animal(string name)
	{
		Name = name;
	}

	public void Eat()
	{
		Console.WriteLine(Name + "正在吃东西");
	}

	public abstract void Speak();
}

class Dog : Animal
{
	public Dog(string name) : base(name)
	{
	}

	public override void Speak()
	{
		Console.WriteLine(Name + "：汪汪");
	}
}

class Cat : Animal
{
	public Cat(string name) : base(name)
	{
	}

	public override void Speak()
	{
		Console.WriteLine(Name + "：喵喵");
	}
}

Animal a1 = new Dog("旺财");
Animal a2 = new Cat("小花");

a1.Eat();
a1.Speak();

a2.Eat();
a2.Speak();
```

理解：
1. `Animal` 是抽象类，不能直接 `new Animal()`
2. `Eat()` 是普通方法，子类可以直接继承使用
3. `Speak()` 是抽象方法，父类只规定“动物要会叫”
4. `Dog` 和 `Cat` 必须用 `override` 实现自己的 `Speak()`
5. `Animal a1 = new Dog()` 可以配合多态使用

----
# 抽象类不能直接实例化
错误写法：
```csharp
abstract class Animal
{
	public abstract void Speak();
}

Animal animal = new Animal();
```

原因：
	抽象类是不完整的类型，里面可能有没实现的抽象成员
	所以不能直接创建对象

正确用法：
```csharp
Animal animal = new Dog("旺财");
animal.Speak();
```

----
# 抽象方法必须在子类中实现
如果子类不是抽象类，就必须实现父类中所有抽象方法。

```csharp
abstract class Shape
{
	public abstract double GetArea();
}

class Circle : Shape
{
	public double Radius;

	public Circle(double radius)
	{
		Radius = radius;
	}

	public override double GetArea()
	{
		return 3.14 * Radius * Radius;
	}
}
```

错误写法：
```csharp
class Circle : Shape
{

}
```

原因：
	`Circle` 是普通类，但没有实现 `Shape` 要求的 `GetArea()`

----
# 子类也可以继续抽象
如果子类暂时不想实现所有抽象方法，那么这个子类也必须声明成 `abstract`。

```csharp
abstract class Shape
{
	public abstract double GetArea();
	public abstract double GetLength();
}

abstract class RoundShape : Shape
{
	public override double GetLength()
	{
		return 0;
	}

	// GetArea 暂时不实现，所以 RoundShape 也必须是抽象类
}
```

理解：
	普通类必须把抽象成员补完整
	抽象类可以继续把没补完的内容留给下一级子类

----
# 抽象类里可以有什么
抽象类不只是能写抽象方法，也可以写普通成员。

```csharp
abstract class Character
{
	public string Name;
	public int Hp;

	public Character(string name, int hp)
	{
		Name = name;
		Hp = hp;
	}

	public void TakeDamage(int damage)
	{
		Hp -= damage;
		Console.WriteLine(Name + "受到了" + damage + "点伤害");
	}

	public virtual void Move()
	{
		Console.WriteLine(Name + "正在移动");
	}

	public abstract void Attack();
}
```

抽象类中可以有：
1. 成员变量
2. 成员属性
3. 构造函数
4. 普通方法
5. 虚方法
6. 抽象方法
7. 抽象属性、抽象索引器、抽象事件

一句话：
	抽象类可以有“已经写好的公共功能”，也可以有“要求子类必须补上的功能”

----
# 抽象类可以有构造函数
抽象类不能直接 `new`，但它仍然可以有构造函数。

原因：
	创建子类对象时，会先构造父类那一部分

```csharp
abstract class Enemy
{
	public string Name;

	public Enemy(string name)
	{
		Name = name;
		Console.WriteLine("Enemy构造");
	}

	public abstract void Attack();
}

class Boss : Enemy
{
	public Boss(string name) : base(name)
	{
		Console.WriteLine("Boss构造");
	}

	public override void Attack()
	{
		Console.WriteLine(Name + "发动攻击");
	}
}

Boss boss = new Boss("魔王");
```

执行顺序还是：
1. 先执行父类 `Enemy` 构造函数
2. 再执行子类 `Boss` 构造函数

----
# abstract、virtual、override 的关系
`abstract` 方法：
```csharp
public abstract void Attack();
```

特点：
1. 没有方法体
2. 子类必须实现
3. 本身就表示允许被重写，所以子类用 `override`

`virtual` 方法：
```csharp
public virtual void Move()
{
	Console.WriteLine("默认移动");
}
```

特点：
1. 有方法体
2. 子类可以选择重写，也可以不重写
3. 子类重写时也用 `override`

对比：
1. `abstract`：父类不给默认实现，子类必须改
2. `virtual`：父类给默认实现，子类可以改
3. `override`：子类真正改写父类的抽象方法或虚方法

----
# 抽象方法和普通方法的区别
普通方法：
```csharp
public void Eat()
{
	Console.WriteLine("吃东西");
}
```

抽象方法：
```csharp
public abstract void Speak();
```

区别：
1. 普通方法有方法体，抽象方法没有方法体
2. 普通方法可以直接继承使用，抽象方法必须由子类实现
3. 普通方法可以写在普通类或抽象类里，抽象方法只能写在抽象类里
4. 抽象方法天然就是给多态准备的

----
# 抽象属性
抽象的不一定只有方法，也可以是属性。

```csharp
abstract class Weapon
{
	public abstract int Damage { get; }
}

class Sword : Weapon
{
	public override int Damage
	{
		get
		{
			return 10;
		}
	}
}
```

简写：
```csharp
class Bow : Weapon
{
	public override int Damage => 8;
}
```

理解：
	父类规定“武器必须有伤害值”
	不同武器自己决定伤害值是多少

----
# 抽象类和密封类不能同时用
错误写法：
```csharp
sealed abstract class Animal
{

}
```

原因：
1. `abstract` 表示这个类必须被继承后才能补完整
2. `sealed` 表示这个类不能被继承
3. 两个要求互相矛盾

这个点可以和[[密封类]]一起记。

----
# 常见错误
错误1：抽象方法写了方法体
```csharp
abstract class Animal
{
	public abstract void Speak()
	{
		Console.WriteLine("动物叫");
	}
}
```

抽象方法不能有方法体。

----
错误2：普通类里写抽象方法
```csharp
class Animal
{
	public abstract void Speak();
}
```

抽象方法只能写在抽象类中。

----
错误3：子类没实现抽象方法
```csharp
abstract class Animal
{
	public abstract void Speak();
}

class Dog : Animal
{

}
```

`Dog` 如果是普通类，就必须 `override Speak()`。

----
错误4：抽象方法上再写 `virtual`
```csharp
abstract class Animal
{
	public abstract virtual void Speak();
}
```

抽象方法本身就表示必须被重写，不需要再写 `virtual`。

----
错误5：直接 new 抽象类
```csharp
Animal animal = new Animal();
```

抽象类不能直接实例化。

----
# 什么时候用抽象类
适合用抽象类的情况：
1. 多个子类确实属于同一种父类
2. 父类有公共字段、属性、构造函数或普通方法可以复用
3. 父类还需要强制子类实现某些行为
4. 你希望调用方通过父类统一调用子类对象

例如：
- `Animal`：动物都能吃，但不同动物叫声不同
- `Shape`：图形都有面积，但不同图形面积算法不同
- `Character`：角色都有血量，但不同角色攻击方式不同

不适合用抽象类的情况：
1. 只是想规定一种能力，例如“能飞”“能攻击”
2. 多个类之间没有明确的父子关系
3. 一个类需要同时拥有多种能力

这些情况后面通常更适合用[[接口]]。

----
# 和多态的关系
抽象类经常和多态一起使用。

```csharp
abstract class Skill
{
	public abstract void Release();
}

class FireSkill : Skill
{
	public override void Release()
	{
		Console.WriteLine("释放火球");
	}
}

class IceSkill : Skill
{
	public override void Release()
	{
		Console.WriteLine("释放冰锥");
	}
}

void UseSkill(Skill skill)
{
	skill.Release();
}

UseSkill(new FireSkill());
UseSkill(new IceSkill());
```

理解：
1. `Skill` 规定所有技能都必须能 `Release()`
2. `FireSkill` 和 `IceSkill` 各自实现释放逻辑
3. `UseSkill` 只依赖抽象父类，不依赖具体技能类

这就是：
	==抽象类定规则，多态负责运行时执行不同实现==

----
# 注意
1. 抽象类不能直接创建对象
2. 抽象方法没有方法体，结尾用分号
3. 有抽象方法的类必须是抽象类
4. 普通子类继承抽象类后，必须实现所有抽象成员
5. 抽象类可以有普通方法、字段、属性和构造函数
6. 抽象方法不能写 `private`，因为子类需要能重写它
7. 抽象方法不能同时写 `virtual`，因为它本身就是虚的
8. 抽象类不能和 `sealed` 同时使用

----
# 一句话记忆
==抽象类不能直接 new，抽象方法只定规则不写实现，子类必须 override。==

再压缩一点：
==父类抽象定规矩，子类重写补细节。==

----
# 面试/复习时怎么说
可以这样答：

“抽象类是用 `abstract` 修饰的类，不能直接实例化，主要用来作为父类。  
它可以包含普通成员，也可以包含抽象成员。抽象方法没有方法体，只声明方法签名，普通子类继承后必须用 `override` 实现。  
抽象类适合用来提取多个子类的共同代码，同时强制子类实现不同的行为，常和继承、多态一起使用。”

----
引用：
1. Microsoft Learn: [abstract (C# Reference)](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/abstract)
2. Microsoft Learn: [Abstract and Sealed Classes and Class Members](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/abstract-and-sealed-classes-and-class-members)
3. Microsoft Learn: [Inheritance - derive types to create more specialized behavior](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/object-oriented/inheritance)

#抽象类
#抽象方法
#核心
