程序设计基础
登录

知识介绍

多态(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();                  // ← 拔出:...

练习题

  1. 写抽象类 Shape(抽象方法 GetArea()、Show()),派生 Triangle、Square、Circle,实例化3个对象放到 Shape[] 数组中,遍历计算面积。

  2. 写接口 IComparable 的简化版(系统有一个,我们自己实现一遍):定义 IMyComparable,方法 int CompareTo(object other),写一个 Student 类(含 Name、Score)实现接口,按分数降序比较。

  3. 写接口 IPay(方法 void Pay(decimal money)),实现 WechatPay、AliPay、CreditCardPay 三个类,再写一个 Checkout 方法接收 IPay 参数,调用 Pay。

  4. 定义接口 IAttackable(void Attack(ITarget target),int Damage { get; })和 ITarget(void TakeDamage(int damage),int Hp { get; set; })。用 Warrior(战士)、Monster(怪物)分别实现这两个接口,演示战士攻击怪物,怪物攻击战士。