# 多态 vob
概念：
	多态是面向对象三大特性之一
	指的是：==同一个父类引用，指向不同子类对象时，调用同一个方法会表现出不同结果==

简单理解：
	调用方只知道“这是一个动物”，但真正执行时，会根据对象实际是狗、猫还是鸟，执行各自的叫声

`vob` 可以这样记：
- `v`：`virtual`，父类声明虚方法
- `o`：`override`，子类重写父类虚方法
- `b`：`base`，子类中调用父类原本的成员

通常用法：
1. 父类提供统一入口
2. 子类根据自己的特点重写行为
3. 调用方只面向父类或接口写代码，不关心具体子类
4. 减少大量 `if/else` 类型判断

----
# 语法
父类：
```csharp
class 父类名
{
	public virtual void 方法名()
	{

	}
}
```

子类：
```csharp
class 子类名 : 父类名
{
	public override void 方法名()
	{

	}
}
```

调用：
```csharp
父类名 变量名 = new 子类名();
变量名.方法名();
```

----
# 基础案例
```csharp
class Animal
{
	public virtual void Speak()
	{
		Console.WriteLine("动物发出声音");
	}
}

class Dog : Animal
{
	public override void Speak()
	{
		Console.WriteLine("狗：汪汪");
	}
}

class Cat : Animal
{
	public override void Speak()
	{
		Console.WriteLine("猫：喵喵");
	}
}

Animal a1 = new Dog();
Animal a2 = new Cat();

a1.Speak(); // 狗：汪汪
a2.Speak(); // 猫：喵喵
```

理解：
- `a1` 和 `a2` 的变量类型都是 `Animal`
- 但是它们实际指向的对象分别是 `Dog` 和 `Cat`
- 调用 `Speak()` 时，会执行实际对象对应的重写版本

这就是多态。

----
# 为什么要用多态
不用多态时，代码可能会这样：
```csharp
void MakeSpeak(Animal animal)
{
	if (animal is Dog)
	{
		Console.WriteLine("狗：汪汪");
	}
	else if (animal is Cat)
	{
		Console.WriteLine("猫：喵喵");
	}
}
```

问题：
1. 每增加一个新动物，就要改 `MakeSpeak`
2. 判断会越来越多
3. 行为分散在外部函数里，子类自己的特点没有放回子类中

使用多态后：
```csharp
void MakeSpeak(Animal animal)
{
	animal.Speak();
}
```

以后新增子类：
```csharp
class Bird : Animal
{
	public override void Speak()
	{
		Console.WriteLine("鸟：叽叽");
	}
}
```

调用方不用改：
```csharp
MakeSpeak(new Bird());
```

这就是多态最重要的价值：
	==把变化交给子类，把稳定入口留给父类==

----
# virtual
作用：
	`virtual` 用在父类成员上，表示这个成员允许被子类重写

```csharp
class Character
{
	public virtual void Attack()
	{
		Console.WriteLine("角色攻击");
	}
}
```

注意：
1. 没有写 `virtual` 的普通方法，子类不能用 `override` 重写
2. `virtual` 常用于方法、属性、索引器、事件
3. 构造函数不能是 `virtual`
4. 静态方法不能被 `override`

----
# override
作用：
	`override` 用在子类成员上，表示重写父类的虚成员

```csharp
class Warrior : Character
{
	public override void Attack()
	{
		Console.WriteLine("战士挥剑攻击");
	}
}
```

重写要求：
1. 父类成员必须是 `virtual`、`abstract` 或已经是 `override`
2. 子类方法名、参数列表、返回类型要匹配
3. 访问修饰符通常要保持一致，例如父类是 `public`，子类也要是 `public`
4. `override` 之后，这个方法仍然可以继续被更下一级子类重写，除非加 `sealed`

----
# base
作用：
	`base` 用在子类内部，表示访问父类成员

常见用途：
1. 调用父类构造函数
2. 在重写方法中，先执行父类原本逻辑，再补充子类逻辑

```csharp
class Character
{
	public virtual void Attack()
	{
		Console.WriteLine("消耗体力");
	}
}

class Warrior : Character
{
	public override void Attack()
	{
		base.Attack();
		Console.WriteLine("战士挥剑攻击");
	}
}

Warrior w = new Warrior();
w.Attack();
```

输出：
```text
消耗体力
战士挥剑攻击
```

理解：
	`base.Attack()` 不是创建父类对象，而是在子类方法里调用父类原本的 `Attack`

----
# 多态调用看实际对象
多态最关键的一句：
	==编译时看变量类型，运行时看实际对象==

```csharp
Animal animal = new Dog();
animal.Speak();
```

这里：
1. 编译时，`animal` 的类型是 `Animal`
2. 所以只能直接调用 `Animal` 中声明过的成员
3. 运行时，`animal` 实际指向的是 `Dog`
4. 所以 `Speak()` 执行的是 `Dog` 重写后的版本

示例：
```csharp
class Dog : Animal
{
	public override void Speak()
	{
		Console.WriteLine("狗叫");
	}

	public void Bite()
	{
		Console.WriteLine("狗咬人");
	}
}

Animal animal = new Dog();
animal.Speak(); // 可以，多态调用
// animal.Bite(); // 错误，Animal 类型里没有 Bite
```

如果确实要调用 `Dog` 独有方法，需要类型转换：
```csharp
Dog dog = animal as Dog;
if (dog != null)
{
	dog.Bite();
}
```

但如果代码经常需要这样转，通常要回头检查抽象设计是不是不够好。

----
# override 和 new 的区别
`override` 是真正的多态重写。

```csharp
class Father
{
	public virtual void Say()
	{
		Console.WriteLine("Father");
	}
}

class Son : Father
{
	public override void Say()
	{
		Console.WriteLine("Son");
	}
}

Father f = new Son();
f.Say(); // Son
```

`new` 是隐藏父类成员，不是真正的多态。

```csharp
class Father
{
	public void Say()
	{
		Console.WriteLine("Father");
	}
}

class Son : Father
{
	public new void Say()
	{
		Console.WriteLine("Son");
	}
}

Father f = new Son();
f.Say(); // Father
```

区别：
1. `override`：看实际对象，所以会调用 `Son.Say()`
2. `new`：看变量类型，所以 `Father f` 调用的是 `Father.Say()`
3. 想要多态效果，用 `virtual + override`

----
# 常见错误
错误1：父类没写 `virtual`，子类却写 `override`
```csharp
class Animal
{
	public void Speak()
	{
	}
}

class Dog : Animal
{
	public override void Speak()
	{
	}
}
```

错误原因：
	父类的 `Speak` 不是虚方法，不能被重写

----
错误2：以为子类独有成员也能通过父类变量直接调用
```csharp
Animal animal = new Dog();
// animal.Bite(); // 错误
```

原因：
	父类变量只能看到父类声明过的成员

----
错误3：用 `new` 代替 `override`
```csharp
class Dog : Animal
{
	public new void Speak()
	{
		Console.WriteLine("狗叫");
	}
}
```

这样只是隐藏，不是真正的多态。

----
# 和里氏替换原则的关系
多态能成立的前提是：
	==子类真的能当父类来用==

比如：
```csharp
void MakeSpeak(Animal animal)
{
	animal.Speak();
}
```

这个函数相信：所有 `Animal` 都能 `Speak()`。

如果某个子类重写后直接抛异常，或者完全破坏父类原本承诺的行为，就会影响[[里氏替换原则]]。

所以可以这样联系：
1. [[继承]]提供父子类型关系
2. 多态让同一个调用产生不同表现
3. [[里氏替换原则]]约束子类不要乱改父类承诺

----
# 注意
1. 多态主要依赖 `virtual + override`
2. 父类引用可以指向子类对象，这是多态的基础
3. 调用重写方法时，看的是对象的实际类型
4. 父类变量只能访问父类中声明过的成员
5. `base` 是在子类中访问父类成员，不是创建父类对象
6. `new` 是隐藏成员，不等于 `override`
7. 如果父类方法不希望再被重写，可以在子类重写时写 `sealed override`

----
# 一句话记忆
==父类定规则，子类改表现，调用时看实际对象。==

再压缩一点：
==virtual 允许改，override 真正改，base 调父类。==

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

“多态是指同一个父类引用指向不同子类对象时，调用同一个虚方法会执行不同子类的重写实现。  
在 C# 中通常通过 `virtual` 和 `override` 实现，父类用 `virtual` 声明可重写方法，子类用 `override` 提供自己的实现。  
如果子类重写时还想保留父类原本逻辑，可以用 `base` 调用父类方法。多态的好处是调用方可以面向父类编程，新增子类时尽量少改原有代码。”

----
引用：
1. Microsoft Learn: [Polymorphism](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/object-oriented/polymorphism)
2. Microsoft Learn: [virtual (C# Reference)](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/virtual)
3. Microsoft Learn: [override (C# Reference)](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/override)
4. Microsoft Learn: [The base keyword](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/base)
5. Microsoft Learn: [Knowing When to Use Override and New Keywords](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/knowing-when-to-use-override-and-new-keywords)

#多态
#核心
