< 返回教程列表
2.4 多态
知识介绍
多态(Polymorphism)= 同一接口,不同实现。
实现多态的三个前提:① 继承 ② 子类 override 重写 ③ 父类引用指向子类对象。
virtual / override 实现多态:
class Animal
{
public string Name { get; set; }
// virtual:虚方法,允许子类重写
public virtual void MakeSound()
{
Console.WriteLine($"{Name}发出了叫声");
}
}
class Dog : Animal
{
// override:重写父类虚方法
public override void MakeSound()
{
Console.WriteLine($"{Name}汪汪汪!");
}
}
class Cat : Animal
{
public override void MakeSound()
{
Console.WriteLine($"{Name}喵喵喵!");
}
}
// 关键:父类引用指向子类对象
Animal a1 = new Dog { Name = "旺财" };
Animal a2 = new Cat { Name = "咪咪" };
a1.MakeSound(); // 旺财汪汪汪!—— 调用的是 Dog 版本
a2.MakeSound(); // 咪咪喵喵喵!—— 调用的是 Cat 版本
多态的好处:统一处理,方便扩展。
Animal[] zoo = { new Dog{Name="旺财"}, new Cat{Name="咪咪"}, new Dog{Name="大黄"} };
foreach (Animal a in zoo)
{
a.MakeSound(); // 循环一次调用,自动识别是什么动物
}
抽象类 abstract(不能实例化,专门当父类用):
abstract class Shape
{
public string Color { get; set; }
// 抽象方法:只有声明,没有实现,子类必须实现
public abstract double GetArea();
public abstract double GetPerimeter();
}
class Circle : Shape
{
public double R { get; set; }
public override double GetArea() => Math.PI * R * R;
public override double GetPerimeter() => 2 * Math.PI * R;
}
class Rectangle : Shape
{
public double W { get; set; }
public double H { get; set; }
public override double GetArea() => W * H;
public override double GetPerimeter() => 2 * (W + H);
}
接口 interface(行为的规范,全是"抽象的"):
// 接口名通常以 I 开头
interface IFlyable
{
void Fly(); // 接口方法默认 public abstract,不用写
int MaxHeight { get; set; }
}
interface ISwimmable
{
void Swim();
}
// 一个类可以继承多个接口(多继承的替代方案)
class Duck : Animal, IFlyable, ISwimmable
{
public int MaxHeight { get; set; }
public void Fly() { Console.WriteLine($"{Name}飞啦,高度{MaxHeight}米"); }
public void Swim() { Console.WriteLine($"{Name}在水里游"); }
public override void MakeSound() { Console.WriteLine($"{Name}嘎嘎嘎!"); }
}
// 多态也可用接口实现
IFlyable f = new Duck { Name = "唐老鸭", MaxHeight = 100 };
f.Fly();
抽象类 vs 接口:
| 对比项 | 抽象类 abstract | 接口 interface |
|---|---|---|
| 能否有字段 | 能 | 不能 |
| 能否有实现方法 | 能 | C# 8.0后可以有默认实现 |
| 构造方法 | 有 | 无 |
| 一个类能继承几个 | 1个 | 多个 |
| 用来描述 | "是什么" | "能做什么" |
例题
例题: 用多态实现电脑的USB设备插拔。
interface IUsbDevice
{
string DeviceName { get; }
void OnPlugIn(); // 插入时
void OnUnplug(); // 拔出时
}
class Mouse : IUsbDevice
{
public string DeviceName => "罗技鼠标";
public void OnPlugIn() => Console.WriteLine($"[{DeviceName}] 已连接,可以点击使用");
public void OnUnplug() => Console.WriteLine($"[{DeviceName}] 已断开");
}
class Keyboard : IUsbDevice
{
public string DeviceName => "机械键盘";
public void OnPlugIn() => Console.WriteLine($"[{DeviceName}] 已连接,可以打字");
public void OnUnplug() => Console.WriteLine($"[{DeviceName}] 已断开");
}
class UsbDisk : IUsbDevice
{
public string DeviceName => "U盘(128G)";
public void OnPlugIn() => Console.WriteLine($"[{DeviceName}] 已识别,可以读写文件");
public void OnUnplug() => Console.WriteLine($"[{DeviceName}] 已安全弹出");
}
class Computer
{
// 定义USB插槽:统一用 IUsbDevice 接口,不管具体是什么设备
private List<IUsbDevice> _ports = new List<IUsbDevice>();
public void PlugDevice(IUsbDevice dev)
{
_ports.Add(dev);
Console.Write("→ 插入:");
dev.OnPlugIn();
}
public void UnplugAll()
{
foreach (var dev in _ports)
{
Console.Write("← 拔出:");
dev.OnUnplug();
}
_ports.Clear();
}
}
// 使用
Computer pc = new Computer();
pc.PlugDevice(new Mouse()); // → 插入:[罗技鼠标] 已连接,可以点击使用
pc.PlugDevice(new Keyboard()); // → 插入:[机械键盘] 已连接,可以打字
pc.PlugDevice(new UsbDisk()); // → 插入:[U盘(128G)] 已识别,可以读写文件
pc.UnplugAll(); // ← 拔出:...
练习题
写抽象类
Shape(抽象方法GetArea()、Show()),派生Triangle、Square、Circle,实例化3个对象放到Shape[]数组中,遍历计算面积。写接口
IComparable的简化版(系统有一个,我们自己实现一遍):定义IMyComparable,方法int CompareTo(object other),写一个Student类(含Name、Score)实现接口,按分数降序比较。写接口
IPay(方法void Pay(decimal money)),实现WechatPay、AliPay、CreditCardPay三个类,再写一个Checkout方法接收IPay参数,调用Pay。定义接口
IAttackable(void Attack(ITarget target),int Damage { get; })和ITarget(void TakeDamage(int damage),int Hp { get; set; })。用Warrior(战士)、Monster(怪物)分别实现这两个接口,演示战士攻击怪物,怪物攻击战士。