目录

第六章 继承与多态

6.1 继承基础

什么是继承

继承是面向对象编程的核心特性之一,允许一个类(子类/派生类)继承另一个类(父类/基类)的成员。

// 基类
public class Animal
{
    public string Name { get; set; }
    public int Age { get; set; }
 
    public void Eat()
    {
        Console.WriteLine($"{Name} 正在吃东西");
    }
 
    public void Sleep()
    {
        Console.WriteLine($"{Name} 正在睡觉");
    }
 
    public virtual void MakeSound()
    {
        Console.WriteLine("动物发出声音");
    }
}
 
// 派生类
public class Dog : Animal
{
    public string Breed { get; set; }
 
    public void Fetch()
    {
        Console.WriteLine($"{Name} 正在捡球");
    }
 
    // 重写基类方法
    public override void MakeSound()
    {
        Console.WriteLine($"{Name} 说: 汪汪!");
    }
}
 
// 使用
Dog dog = new Dog { Name = "小黑", Age = 3, Breed = "金毛" };
dog.Eat();        // 继承自Animal
dog.Fetch();      // Dog自己的方法
dog.MakeSound();  // 调用重写的方法

构造函数继承

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
 
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
}
 
public class Employee : Person
{
    public string EmployeeId { get; set; }
    public decimal Salary { get; set; }
 
    // 调用基类构造函数
    public Employee(string name, int age, string employeeId, decimal salary) 
        : base(name, age)
    {
        EmployeeId = employeeId;
        Salary = salary;
    }
}
 
// 使用
Employee emp = new Employee("张三", 30, "E001", 5000);

6.2 方法重写(Override)

virtual和override

public class Shape
{
    public string Name { get; set; }
 
    // 虚方法:允许子类重写
    public virtual double CalculateArea()
    {
        return 0;
    }
 
    public virtual void Draw()
    {
        Console.WriteLine($"绘制{Name}");
    }
}
 
public class Circle : Shape
{
    public double Radius { get; set; }
 
    public Circle(double radius)
    {
        Name = "圆形";
        Radius = radius;
    }
 
    // 重写基类方法
    public override double CalculateArea()
    {
        return Math.PI * Radius * Radius;
    }
 
    public override void Draw()
    {
        base.Draw();  // 调用基类实现
        Console.WriteLine($"半径: {Radius}");
    }
}
 
public class Rectangle : Shape
{
    public double Width { get; set; }
    public double Height { get; set; }
 
    public Rectangle(double width, double height)
    {
        Name = "矩形";
        Width = width;
        Height = height;
    }
 
    public override double CalculateArea()
    {
        return Width * Height;
    }
}

sealed关键字

public class BaseClass
{
    public virtual void Method() { }
}
 
public class DerivedClass : BaseClass
{
    // 阻止进一步重写
    public sealed override void Method() { }
}
 
// 无法重写sealed方法
// public class FurtherDerived : DerivedClass
// {
//     public override void Method() { }  // 编译错误
// }

6.3 抽象类

抽象类和抽象方法

// 抽象类:不能被实例化
public abstract class Vehicle
{
    public string Brand { get; set; }
    public string Model { get; set; }
 
    // 普通方法
    public void Start()
    {
        Console.WriteLine($"{Brand} {Model} 启动");
    }
 
    // 抽象方法:必须在子类中实现
    public abstract void Drive();
 
    // 抽象属性
    public abstract int MaxSpeed { get; }
}
 
public class Car : Vehicle
{
    public int TrunkCapacity { get; set; }
 
    // 实现抽象方法
    public override void Drive()
    {
        Console.WriteLine("汽车在公路上行驶");
    }
 
    public override int MaxSpeed => 200;
}
 
public class Motorcycle : Vehicle
{
    public bool HasSidecar { get; set; }
 
    public override void Drive()
    {
        Console.WriteLine("摩托车灵活穿梭");
    }
 
    public override int MaxSpeed => 150;
}
 
// 使用
Vehicle myCar = new Car { Brand = "Toyota", Model = "Corolla" };
Vehicle myBike = new Motorcycle { Brand = "Honda", Model = "CBR" };
 
myCar.Drive();   // 输出: 汽车在公路上行驶
myBike.Drive();  // 输出: 摩托车灵活穿梭

6.4 接口

接口定义

接口定义一组行为规范,类可以实现多个接口。

// 定义接口
public interface IFlyable
{
    void Fly();
    int MaxAltitude { get; }
}
 
public interface ISwimmable
{
    void Swim();
    int MaxDepth { get; }
}
 
public interface IHuntable
{
    void Hunt();
}

实现接口

// 实现多个接口
public class Duck : Animal, IFlyable, ISwimmable
{
    public int MaxAltitude => 1000;
    public int MaxDepth => 5;
 
    public void Fly()
    {
        Console.WriteLine($"{Name} 在飞翔");
    }
 
    public void Swim()
    {
        Console.WriteLine($"{Name} 在游泳");
    }
 
    public override void MakeSound()
    {
        Console.WriteLine($"{Name} 说: 嘎嘎!");
    }
}
 
// 使用
Duck duck = new Duck { Name = "唐老鸭" };
duck.Fly();
duck.Swim();
duck.MakeSound();

显式接口实现

public interface IEnglish
{
    void Speak();
}
 
public interface IChinese
{
    void Speak();
}
 
public class BilingualPerson : IEnglish, IChinese
{
    // 显式实现接口
    void IEnglish.Speak()
    {
        Console.WriteLine("Hello!");
    }
 
    void IChinese.Speak()
    {
        Console.WriteLine("你好!");
    }
 
    // 默认实现
    public void Speak()
    {
        Console.WriteLine("Speaking...");
    }
}
 
// 使用
var person = new BilingualPerson();
person.Speak();  // 调用默认实现
 
IEnglish english = person;
english.Speak();  // 调用英语实现
 
IChinese chinese = person;
chinese.Speak();  // 调用中文实现

6.5 多态

编译时多态(重载)

public class Calculator
{
    // 方法重载
    public int Add(int a, int b) => a + b;
    public double Add(double a, double b) => a + b;
    public int Add(int a, int b, int c) => a + b + c;
    public string Add(string a, string b) => a + b;
}

运行时多态(重写)

public class PolymorphismDemo
{
    public static void Main()
    {
        // 使用基类引用指向派生类对象
        Shape shape1 = new Circle(5);
        Shape shape2 = new Rectangle(4, 6);
 
        // 运行时确定调用哪个方法
        Console.WriteLine($"形状1面积: {shape1.CalculateArea():F2}");
        Console.WriteLine($"形状2面积: {shape2.CalculateArea():F2}");
 
        // 数组多态
        Shape[] shapes = new Shape[]
        {
            new Circle(3),
            new Rectangle(4, 5),
            new Circle(2)
        };
 
        double totalArea = 0;
        foreach (Shape shape in shapes)
        {
            totalArea += shape.CalculateArea();
            shape.Draw();
            Console.WriteLine();
        }
        Console.WriteLine($"总面积: {totalArea:F2}");
    }
}

6.6 类型转换和判断

is运算符

Animal animal = new Dog { Name = "小黑" };
 
// 类型检查
if (animal is Dog)
{
    Dog dog = (Dog)animal;
    dog.Fetch();
}
 
// 模式匹配(C# 7+)
if (animal is Dog d)
{
    d.Fetch();
}
 
// null检查
if (animal is not null)
{
    Console.WriteLine("animal不为null");
}

as运算符

Animal animal = new Dog { Name = "小黑" };
 
// 安全类型转换
Dog dog = animal as Dog;
if (dog != null)
{
    dog.Fetch();
}
 
// 转换失败返回null
Cat cat = animal as Cat;  // cat为null

强制类型转换

Animal animal = new Dog { Name = "小黑" };
 
// 强制转换
Dog dog = (Dog)animal;  // 成功
 
// 转换失败抛出异常
// Cat cat = (Cat)animal;  // InvalidCastException

6.7 抽象类 vs 接口

特性 抽象类 接口
————–——
实例化 不能 不能
实现 单继承 多实现
成员 可以有实现 C# 8+可以有默认实现
字段 可以有 不能有实例字段
构造函数 可以有 不能有
访问修饰符 可以指定 默认public
使用场景 “is-a”关系 “can-do”关系
// 抽象类示例 - "is-a"关系
public abstract class Bird : Animal
{
    public void LayEgg()
    {
        Console.WriteLine("下蛋");
    }
}
 
// 接口示例 - "can-do"关系
public interface ILogger
{
    void Log(string message);
}
 
public class FileLogger : ILogger
{
    public void Log(string message)
    {
        File.AppendAllText("log.txt", message + Environment.NewLine);
    }
}

6.8 实战:图形系统

// 可绘制接口
public interface IDrawable
{
    void Draw();
    ConsoleColor Color { get; set; }
}
 
// 抽象基类
public abstract class Shape : IDrawable
{
    public string Name { get; set; }
    public ConsoleColor Color { get; set; } = ConsoleColor.White;
 
    // 抽象方法
    public abstract double CalculateArea();
    public abstract double CalculatePerimeter();
 
    // 虚方法
    public virtual void Draw()
    {
        Console.ForegroundColor = Color;
        Console.WriteLine($"绘制{Name}");
        Console.WriteLine($"  面积: {CalculateArea():F2}");
        Console.WriteLine($"  周长: {CalculatePerimeter():F2}");
        Console.ResetColor();
    }
 
    // 具体方法
    public void PrintInfo()
    {
        Console.WriteLine($"形状: {Name}");
        Console.WriteLine($"颜色: {Color}");
    }
}
 
// 圆形
public class Circle : Shape
{
    public double Radius { get; set; }
 
    public Circle(double radius)
    {
        Name = "圆形";
        Radius = radius;
    }
 
    public override double CalculateArea() => Math.PI * Radius * Radius;
    public override double CalculatePerimeter() => 2 * Math.PI * Radius;
 
    public override void Draw()
    {
        base.Draw();
        Console.WriteLine($"  半径: {Radius}");
    }
}
 
// 矩形
public class Rectangle : Shape
{
    public double Width { get; set; }
    public double Height { get; set; }
 
    public Rectangle(double width, double height)
    {
        Name = "矩形";
        Width = width;
        Height = height;
    }
 
    public override double CalculateArea() => Width * Height;
    public override double CalculatePerimeter() => 2 * (Width + Height);
 
    public bool IsSquare => Width == Height;
 
    public override void Draw()
    {
        base.Draw();
        Console.WriteLine($"  宽: {Width}, 高: {Height}");
        if (IsSquare)
        {
            Console.WriteLine("  这是一个正方形!");
        }
    }
}
 
// 三角形
public class Triangle : Shape
{
    public double SideA { get; set; }
    public double SideB { get; set; }
    public double SideC { get; set; }
 
    public Triangle(double a, double b, double c)
    {
        Name = "三角形";
        SideA = a;
        SideB = b;
        SideC = c;
    }
 
    public override double CalculateArea()
    {
        // 海伦公式
        double s = CalculatePerimeter() / 2;
        return Math.Sqrt(s * (s - SideA) * (s - SideB) * (s - SideC));
    }
 
    public override double CalculatePerimeter() => SideA + SideB + SideC;
}
 
// 图形管理器
public class ShapeManager
{
    private List<Shape> shapes = new List<Shape>();
 
    public void AddShape(Shape shape)
    {
        shapes.Add(shape);
    }
 
    public void DrawAll()
    {
        Console.WriteLine("===== 绘制所有图形 =====");
        foreach (var shape in shapes)
        {
            shape.Draw();
            Console.WriteLine();
        }
    }
 
    public double GetTotalArea()
    {
        return shapes.Sum(s => s.CalculateArea());
    }
 
    public Shape FindLargest()
    {
        return shapes.OrderByDescending(s => s.CalculateArea()).FirstOrDefault();
    }
 
    public IEnumerable<T> GetShapesOfType<T>() where T : Shape
    {
        return shapes.OfType<T>();
    }
}
 
// 使用示例
public class ShapeDemo
{
    public static void Main()
    {
        var manager = new ShapeManager();
 
        manager.AddShape(new Circle(5) { Color = ConsoleColor.Red });
        manager.AddShape(new Rectangle(4, 6) { Color = ConsoleColor.Green });
        manager.AddShape(new Rectangle(5, 5) { Color = ConsoleColor.Blue });
        manager.AddShape(new Triangle(3, 4, 5) { Color = ConsoleColor.Yellow });
 
        manager.DrawAll();
 
        Console.WriteLine($"总面积: {manager.GetTotalArea():F2}");
 
        var largest = manager.FindLargest();
        Console.WriteLine($"最大图形: {largest.Name}");
 
        Console.WriteLine("\n所有矩形:");
        foreach (var rect in manager.GetShapesOfType<Rectangle>())
        {
            Console.WriteLine($"  - {rect.Width}x{rect.Height}");
        }
    }
}

练习题

基础练习

1. 动物类层次:创建动物类层次结构:

  1. 基类Animal(Name, Eat(), Sleep())
  2. 派生类Mammal(有毛发)、Bird(有羽毛)、Fish(有鳞片)
  3. 每个派生类重写MakeSound()
  4. 创建至少两个具体动物类(如Dog、Eagle、Shark)

2. 员工管理系统扩展:在第五章员工类基础上:

  1. 创建Employee抽象基类
  2. 实现SalariedEmployee、HourlyEmployee、CommissionEmployee
  3. 每个类实现抽象的CalculatePay()方法
  4. 创建Employee数组,多态调用CalculatePay()

3. 接口实现:实现以下接口和类:

  1. IPayable接口(有CalculatePayment()方法)
  2. Invoice类实现IPayable
  3. Employee类也实现IPayable
  4. 创建IPayable数组,统一处理不同类型的应付款项

进阶练习

4. 游戏角色系统:设计RPG游戏角色系统:

  1. 抽象基类Character(Name, Health, Attack(), Defend())
  2. 派生类:Warrior、Mage、Archer
  3. 接口:ICanCastSpell、ICanStealth
  4. 实现不同角色的特殊能力
  5. 创建战斗系统,角色可以互相攻击

5. 插件系统:实现一个简单的插件架构:

  1. IPlugin接口(Name, Version, Initialize(), Execute())
  2. 多个具体插件实现
  3. 插件管理器动态加载和执行插件
  4. 支持插件配置

6. 绘图应用:扩展本章的图形系统:

  1. 添加更多形状(椭圆、多边形)
  2. 实现图层概念(Layer类包含多个Shape)
  3. 实现序列化和反序列化
  4. 添加撤销/重做功能

挑战练习

7. 动物园管理系统:设计完整的动物园管理程序:

  1. Animal抽象基类
  2. 多种具体动物类
  3. 饲养员类(可以喂养特定类型的动物)
  4. 笼子/栖息地类
  5. 实现动物的饮食、健康、繁殖系统

8. 排序策略模式:实现多种排序算法:

  1. ISortStrategy接口
  2. BubbleSort、QuickSort、MergeSort等实现
  3. 允许运行时切换排序策略
  4. 性能比较

本章小结

本章学习了C#的继承与多态:

  1. 继承的语法和构造函数链
  2. virtual和override实现方法重写
  3. 抽象类和抽象方法
  4. 接口的定义和实现
  5. 编译时多态(重载)和运行时多态(重写)
  6. 类型转换和判断
  7. 抽象类与接口的区别

这些是面向对象编程的核心概念,理解它们对设计良好的软件架构至关重要。