C# 零基础到精通教程 - 第八章:面向对象编程(进阶)——继承与多态

8.1 继承(Inheritance)

8.1.1 为什么需要继承?

没有继承的问题:代码重复

csharp

复制代码
// 学生类
class Student
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string StudentId { get; set; }
    
    public void Eat() { Console.WriteLine($"{Name}正在吃饭"); }
    public void Sleep() { Console.WriteLine($"{Name}正在睡觉"); }
    public void Study() { Console.WriteLine($"{Name}正在学习"); }
}

// 老师类——大量重复代码!
class Teacher
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string TeacherId { get; set; }
    
    public void Eat() { Console.WriteLine($"{Name}正在吃饭"); }
    public void Sleep() { Console.WriteLine($"{Name}正在睡觉"); }
    public void Teach() { Console.WriteLine($"{Name}正在教书"); }
}

使用继承:复用公共代码

csharp

复制代码
// 基类(父类):包含公共的属性和方法
class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    
    public void Eat() { Console.WriteLine($"{Name}正在吃饭"); }
    public void Sleep() { Console.WriteLine($"{Name}正在睡觉"); }
}

// 派生类(子类):继承 Person,并添加自己的成员
class Student : Person  // : 表示继承
{
    public string StudentId { get; set; }
    public void Study() { Console.WriteLine($"{Name}正在学习"); }
}

class Teacher : Person
{
    public string TeacherId { get; set; }
    public void Teach() { Console.WriteLine($"{Name}正在教书"); }
}

// 使用
Student stu = new Student();
stu.Name = "张三";      // 来自 Person
stu.Age = 20;           // 来自 Person
stu.StudentId = "001";  // 自己的
stu.Eat();              // 来自 Person
stu.Study();            // 自己的

8.1.2 继承的基本概念

text

复制代码
┌─────────────────────────────────────────────────────────┐
│                         Person                           │
│  (基类/父类/超类)                                        │
│  ┌───────────────────────────────────────────────────┐  │
│  │ + Name : string                                    │  │
│  │ + Age : int                                        │  │
│  │ + Eat()                                            │  │
│  │ + Sleep()                                          │  │
│  └───────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────┘
                           ▲
                           │ 继承
           ┌───────────────┴───────────────┐
           │                               │
┌──────────────────────────┐  ┌──────────────────────────┐
│        Student           │  │        Teacher           │
│  (派生类/子类)           │  │  (派生类/子类)           │
│  ┌─────────────────────┐ │  │  ┌─────────────────────┐ │
│  │ + StudentId : string│ │  │  │ + TeacherId : string│ │
│  │ + Study()           │ │  │  │ + Teach()           │ │
│  └─────────────────────┘ │  │  └─────────────────────┘ │
└──────────────────────────┘  └──────────────────────────┘

术语

  • 基类(Base Class):被继承的类,也叫父类

  • 派生类(Derived Class):继承的类,也叫子类

  • 继承链:类可以有多层继承

8.1.3 继承的语法

csharp

复制代码
// 基本语法
class 派生类 : 基类
{
    // 新增成员
    // 重写基类成员
}

// 示例
class Animal
{
    public string Name { get; set; }
    public void Eat() { Console.WriteLine($"{Name}在吃东西"); }
}

class Dog : Animal
{
    public void Bark() { Console.WriteLine($"{Name}在汪汪叫"); }
}

class Puppy : Dog  // 可以多层继承
{
    public void Play() { Console.WriteLine($"{Name}在玩耍"); }
}

8.1.4 子类访问父类成员

csharp

复制代码
class Vehicle
{
    public string Brand { get; set; }
    public int Speed { get; protected set; }  // protected:子类可访问
    
    public void Start()
    {
        Console.WriteLine("车辆启动");
    }
    
    private void SecretMethod()  // private:子类不能访问
    {
        Console.WriteLine("这是秘密方法");
    }
}

class Car : Vehicle
{
    public int Doors { get; set; }
    
    public void Accelerate()
    {
        Speed += 10;  // ✅ 可以访问 protected 成员
        Console.WriteLine($"{Brand}加速到{Speed}km/h");
        Start();      // ✅ 可以访问 public 成员
        // SecretMethod();  // ❌ private 成员不能访问
    }
}

8.1.5 访问修饰符在继承中的影响

修饰符 基类内部 派生类内部 外部代码
public
protected
private
internal 同一程序集内可访问
protected internal ✅(同一程序集或派生类)

csharp

复制代码
class BaseClass
{
    public int publicField;
    protected int protectedField;
    private int privateField;
    internal int internalField;
    protected internal int protectedInternalField;
    
    void Test()
    {
        publicField = 1;           // ✅
        protectedField = 2;        // ✅
        privateField = 3;          // ✅
        internalField = 4;         // ✅
        protectedInternalField = 5; // ✅
    }
}

class DerivedClass : BaseClass
{
    void Test()
    {
        publicField = 1;           // ✅
        protectedField = 2;        // ✅
        // privateField = 3;       // ❌
        internalField = 4;         // ✅(同一程序集)
        protectedInternalField = 5; // ✅
    }
}

8.1.6 构造函数与继承

重要规则

  1. 创建子类对象时,会先调用父类的构造函数,再调用子类的构造函数

  2. 如果父类没有无参构造函数,子类必须显式调用父类的构造函数

csharp

复制代码
class Animal
{
    public string Name { get; set; }
    
    // 无参构造函数
    public Animal()
    {
        Console.WriteLine("Animal 无参构造函数被调用");
        Name = "未知动物";
    }
    
    // 带参构造函数
    public Animal(string name)
    {
        Console.WriteLine($"Animal 带参构造函数被调用,name={name}");
        Name = name;
    }
}

class Dog : Animal
{
    public string Breed { get; set; }
    
    // 隐式调用父类无参构造函数
    public Dog()
    {
        Console.WriteLine("Dog 无参构造函数被调用");
        Breed = "未知品种";
    }
    
    // 使用 base 关键字调用父类的带参构造函数
    public Dog(string name, string breed) : base(name)  // 先调用 Animal(name)
    {
        Console.WriteLine($"Dog 带参构造函数被调用,breed={breed}");
        Breed = breed;
    }
}

// 测试
Dog dog1 = new Dog();
// 输出:
// Animal 无参构造函数被调用
// Dog 无参构造函数被调用

Dog dog2 = new Dog("旺财", "金毛");
// 输出:
// Animal 带参构造函数被调用,name=旺财
// Dog 带参构造函数被调用,breed=金毛

父类没有无参构造函数时

csharp

复制代码
class Parent
{
    public Parent(string message)
    {
        Console.WriteLine($"Parent: {message}");
    }
    // 没有无参构造函数
}

class Child : Parent
{
    // 必须显式调用父类的构造函数
    public Child(string message) : base(message)
    {
        Console.WriteLine("Child 构造函数");
    }
    
    // 或者调用父类的其他构造函数
    public Child() : base("默认消息")
    {
        Console.WriteLine("Child 无参构造函数");
    }
}

8.1.7 base 关键字

base 代表父类对象,用于访问父类的成员

csharp

复制代码
class Animal
{
    public string Name { get; set; }
    
    public virtual void MakeSound()
    {
        Console.WriteLine("动物发出声音");
    }
    
    public Animal(string name)
    {
        Name = name;
    }
}

class Cat : Animal
{
    public string Color { get; set; }
    
    // 调用父类构造函数
    public Cat(string name, string color) : base(name)
    {
        Color = color;
    }
    
    // 调用父类方法
    public override void MakeSound()
    {
        base.MakeSound();  // 先调用父类的方法
        Console.WriteLine($"{Name}喵喵叫");
    }
    
    public void ShowInfo()
    {
        // 访问父类属性
        Console.WriteLine($"名字:{base.Name},颜色:{Color}");
    }
}

8.2 方法重写(Method Overriding)

8.2.1 为什么要重写?

子类需要改变从父类继承来的方法的行为

csharp

复制代码
class Shape
{
    public virtual double GetArea()  // virtual:允许子类重写
    {
        return 0;
    }
    
    public virtual void Draw()
    {
        Console.WriteLine("绘制形状");
    }
}

class Circle : Shape
{
    public double Radius { get; set; }
    
    public Circle(double radius)
    {
        Radius = radius;
    }
    
    public override double GetArea()  // override:重写父类方法
    {
        return Math.PI * Radius * Radius;
    }
    
    public override void Draw()
    {
        Console.WriteLine($"绘制半径为{Radius}的圆");
    }
}

class Rectangle : Shape
{
    public double Width { get; set; }
    public double Height { get; set; }
    
    public Rectangle(double width, double height)
    {
        Width = width;
        Height = height;
    }
    
    public override double GetArea()
    {
        return Width * Height;
    }
    
    public override void Draw()
    {
        Console.WriteLine($"绘制{Width}x{Height}的矩形");
    }
}

8.2.2 virtual 和 override

关键字 用途 说明
virtual 父类中标记方法可以被重写 父类提供默认实现
override 子类中重写父类的方法 提供新的实现
abstract 标记抽象方法(本章后面讲) 父类不提供实现
sealed 阻止进一步重写 方法不能被重写

csharp

复制代码
class Parent
{
    public virtual void Method1()
    {
        Console.WriteLine("Parent.Method1");
    }
    
    public virtual void Method2()
    {
        Console.WriteLine("Parent.Method2");
    }
    
    public void Method3()  // 没有 virtual,不能重写
    {
        Console.WriteLine("Parent.Method3");
    }
}

class Child : Parent
{
    public override void Method1()  // 重写
    {
        Console.WriteLine("Child.Method1");
    }
    
    public override void Method2()  // 重写
    {
        base.Method2();  // 可以调用父类实现
        Console.WriteLine("Child.Method2");
    }
    
    // public override void Method3()  // ❌ 错误!Method3 不是 virtual/abstract
    // {
    // }
    
    public new void Method3()  // new:隐藏父类方法(不是重写)
    {
        Console.WriteLine("Child.Method3");
    }
}

8.2.3 重写 vs 隐藏

csharp

复制代码
class Parent
{
    public virtual void VirtualMethod()
    {
        Console.WriteLine("Parent.VirtualMethod");
    }
    
    public void NormalMethod()
    {
        Console.WriteLine("Parent.NormalMethod");
    }
}

class Child : Parent
{
    public override void VirtualMethod()  // 重写
    {
        Console.WriteLine("Child.VirtualMethod");
    }
    
    public new void NormalMethod()  // 隐藏(建议加上 new 关键字)
    {
        Console.WriteLine("Child.NormalMethod");
    }
}

// 测试
Parent p = new Child();
p.VirtualMethod();   // 输出:Child.VirtualMethod(多态)
p.NormalMethod();    // 输出:Parent.NormalMethod(编译时决定)

Child c = new Child();
c.VirtualMethod();   // 输出:Child.VirtualMethod
c.NormalMethod();    // 输出:Child.NormalMethod

区别总结

特性 重写(override) 隐藏(new)
父类方法需要 virtual
通过父类引用调用 调用子类实现 调用父类实现
运行时行为 多态 编译时决定

8.3 多态(Polymorphism)

8.3.1 什么是多态?

多态 = 同一个方法在不同对象上有不同的表现形式

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("喵喵喵");
    }
}

class Duck : Animal
{
    public override void Speak()
    {
        Console.WriteLine("嘎嘎嘎");
    }
}

// 多态的使用
Animal[] animals = new Animal[]
{
    new Dog(),
    new Cat(),
    new Duck()
};

foreach (Animal animal in animals)
{
    animal.Speak();  // 同一个方法调用,不同表现
}
// 输出:
// 汪汪汪
// 喵喵喵
// 嘎嘎嘎

8.3.2 多态的好处

csharp

复制代码
// 没有多态:需要为每种动物写单独的方法
class AnimalSoundMaker
{
    public void MakeDogSound(Dog dog) { dog.Speak(); }
    public void MakeCatSound(Cat cat) { cat.Speak(); }
    public void MakeDuckSound(Duck duck) { duck.Speak(); }
    // 每增加一种动物,就要加一个方法
}

// 有多态:一个方法处理所有动物
class AnimalSoundMaker
{
    public void MakeSound(Animal animal)  // 参数是父类类型
    {
        animal.Speak();  // 运行时决定调用哪个子类的方法
    }
}

// 使用
AnimalSoundMaker maker = new AnimalSoundMaker();
maker.MakeSound(new Dog());   // 汪汪汪
maker.MakeSound(new Cat());   // 喵喵喵
maker.MakeSound(new Duck());  // 嘎嘎嘎

8.3.3 多态的应用场景

场景1:统一处理不同类型对象

csharp

复制代码
// 员工系统
class Employee
{
    public string Name { get; set; }
    public virtual double CalculateSalary()
    {
        return 3000;  // 基本工资
    }
}

class Manager : Employee
{
    public double Bonus { get; set; }
    
    public override double CalculateSalary()
    {
        return base.CalculateSalary() + Bonus;
    }
}

class Salesperson : Employee
{
    public double SalesAmount { get; set; }
    
    public override double CalculateSalary()
    {
        return base.CalculateSalary() + SalesAmount * 0.05;  // 5%提成
    }
}

class Intern : Employee
{
    public override double CalculateSalary()
    {
        return 1500;  // 实习生工资
    }
}

// 计算所有员工的工资
class PayrollSystem
{
    public void CalculatePayroll(Employee[] employees)
    {
        double total = 0;
        foreach (Employee emp in employees)
        {
            double salary = emp.CalculateSalary();
            Console.WriteLine($"{emp.Name} 工资:{salary}元");
            total += salary;
        }
        Console.WriteLine($"总工资支出:{total}元");
    }
}

场景2:插件/可扩展架构

csharp

复制代码
// 支付系统
abstract class PaymentMethod
{
    public abstract void Pay(double amount);
}

class CreditCardPayment : PaymentMethod
{
    public override void Pay(double amount)
    {
        Console.WriteLine($"使用信用卡支付{amount}元");
    }
}

class AlipayPayment : PaymentMethod
{
    public override void Pay(double amount)
    {
        Console.WriteLine($"使用支付宝支付{amount}元");
    }
}

class WechatPayment : PaymentMethod
{
    public override void Pay(double amount)
    {
        Console.WriteLine($"使用微信支付{amount}元");
    }
}

class PaymentService
{
    public void ProcessPayment(PaymentMethod method, double amount)
    {
        Console.WriteLine("开始处理支付...");
        method.Pay(amount);
        Console.WriteLine("支付完成");
    }
}

// 使用
PaymentService service = new PaymentService();
service.ProcessPayment(new CreditCardPayment(), 100);
service.ProcessPayment(new AlipayPayment(), 200);

8.4 抽象类与抽象方法

8.4.1 什么是抽象类?

抽象类 = 不能被实例化的类,只能作为基类被继承

csharp

复制代码
// 抽象类
abstract class Shape
{
    // 抽象方法:没有方法体,子类必须实现
    public abstract double GetArea();
    
    // 抽象方法:没有方法体
    public abstract double GetPerimeter();
    
    // 普通方法:有方法体,子类可以继承
    public void ShowInfo()
    {
        Console.WriteLine($"面积:{GetArea():F2},周长:{GetPerimeter():F2}");
    }
}

// 抽象类不能被实例化
// Shape s = new Shape();  // ❌ 错误!不能创建抽象类的实例

// 具体类必须实现所有抽象方法
class Circle : Shape
{
    public double Radius { get; set; }
    
    public Circle(double radius)
    {
        Radius = radius;
    }
    
    public override double GetArea()
    {
        return Math.PI * Radius * Radius;
    }
    
    public override double GetPerimeter()
    {
        return 2 * Math.PI * Radius;
    }
}

class Rectangle : Shape
{
    public double Width { get; set; }
    public double Height { get; set; }
    
    public Rectangle(double width, double height)
    {
        Width = width;
        Height = height;
    }
    
    public override double GetArea()
    {
        return Width * Height;
    }
    
    public override double GetPerimeter()
    {
        return 2 * (Width + Height);
    }
}

// 使用
Shape[] shapes = new Shape[]
{
    new Circle(5),
    new Rectangle(4, 6)
};

foreach (Shape shape in shapes)
{
    shape.ShowInfo();
}

8.4.2 抽象类 vs 普通类

特点 普通类 抽象类
可以实例化
可以有抽象方法
可以作为基类
可以包含实现

8.4.3 抽象方法的规则

csharp

复制代码
abstract class BaseClass
{
    // 抽象方法不能有方法体
    public abstract void AbstractMethod();  // ✅ 正确,没有 {}
    
    // public abstract void WrongMethod() { }  // ❌ 错误!抽象方法不能有实现
    
    // 抽象方法不能是 private
    // private abstract void PrivateMethod();  // ❌ 错误!
    
    // 抽象方法可以是 protected 或 internal
    protected abstract void ProtectedMethod();
    internal abstract void InternalMethod();
    
    // 可以同时包含普通方法和抽象方法
    public void NormalMethod()
    {
        Console.WriteLine("普通方法");
    }
}

class DerivedClass : BaseClass
{
    // 必须实现所有抽象方法
    public override void AbstractMethod()
    {
        Console.WriteLine("实现了抽象方法");
    }
    
    protected override void ProtectedMethod()
    {
        Console.WriteLine("实现了 protected 抽象方法");
    }
    
    internal override void InternalMethod()
    {
        Console.WriteLine("实现了 internal 抽象方法");
    }
}

8.5 密封类与密封方法

8.5.1 密封类(sealed class)

密封类不能被继承

csharp

复制代码
sealed class FinalClass
{
    public void Method()
    {
        Console.WriteLine("这个方法不能被重写,这个类不能被继承");
    }
}

// class Derived : FinalClass  // ❌ 错误!不能继承密封类
// {
// }

什么时候用密封类

  • 类不需要被继承

  • 为了安全性或性能考虑

  • .NET 基础库中的 string 就是密封类

8.5.2 密封方法(sealed method)

密封方法阻止子类进一步重写

csharp

复制代码
class Parent
{
    public virtual void Method1()
    {
        Console.WriteLine("Parent.Method1");
    }
    
    public virtual void Method2()
    {
        Console.WriteLine("Parent.Method2");
    }
}

class Child : Parent
{
    public sealed override void Method1()  // 密封这个方法
    {
        Console.WriteLine("Child.Method1");
    }
    
    public override void Method2()
    {
        Console.WriteLine("Child.Method2");
    }
}

class GrandChild : Child
{
    // public override void Method1()  // ❌ 错误!Method1 已被密封
    // {
    // }
    
    public override void Method2()  // ✅ 可以重写 Method2
    {
        Console.WriteLine("GrandChild.Method2");
    }
}

8.6 is 和 as 运算符

8.6.1 is 运算符------类型检查

is 检查对象是否是指定类型(或其派生类型)

csharp

复制代码
class Animal { }
class Dog : Animal { }
class Cat : Animal { }

Animal animal = new Dog();

// 类型检查
bool isDog = animal is Dog;      // true
bool isCat = animal is Cat;      // false
bool isAnimal = animal is Animal; // true(Dog 是 Animal 的派生类)

// 结合声明(C# 7.0+)
if (animal is Dog dog)
{
    Console.WriteLine($"这是一只狗:{dog}");
}

8.6.2 as 运算符------类型转换

as 尝试转换类型,失败时返回 null(不抛出异常)

csharp

复制代码
object obj = "Hello";

// 使用 as 转换
string str = obj as string;
if (str != null)
{
    Console.WriteLine($"转换成功:{str.Length}");  // 5
}

// 转换失败返回 null
object obj2 = 123;
string str2 = obj2 as string;  // null
if (str2 == null)
{
    Console.WriteLine("转换失败");
}

// as vs 强制转换
// 强制转换失败会抛异常
// string str3 = (string)obj2;  // InvalidCastException

// as 转换失败返回 null,不抛异常
string str4 = obj2 as string;  // null,安全

8.6.3 类型转换总结

csharp

复制代码
// 向上转型(隐式):子类 → 父类
Dog dog = new Dog();
Animal animal = dog;  // 自动转换

// 向下转型(显式):父类 → 子类
Animal animal2 = new Dog();

// 方式1:强制转换(可能抛异常)
Dog dog1 = (Dog)animal2;

// 方式2:as 转换(失败返回 null)
Dog dog2 = animal2 as Dog;
if (dog2 != null) { }

// 方式3:is + 强制转换
if (animal2 is Dog dog3)
{
    // dog3 可用
}

8.7 综合示例

示例1:图形绘制系统

csharp

复制代码
using System;
using System.Collections.Generic;

// 抽象基类
abstract class Shape
{
    public string Color { get; set; }
    
    protected Shape(string color)
    {
        Color = color;
    }
    
    // 抽象方法
    public abstract double GetArea();
    public abstract double GetPerimeter();
    public abstract void Draw();
    
    // 虚方法
    public virtual void ShowInfo()
    {
        Console.WriteLine($"形状类型:{GetType().Name}");
        Console.WriteLine($"颜色:{Color}");
        Console.WriteLine($"面积:{GetArea():F2}");
        Console.WriteLine($"周长:{GetPerimeter():F2}");
    }
}

// 圆形
class Circle : Shape
{
    public double Radius { get; set; }
    
    public Circle(double radius, string color) : base(color)
    {
        Radius = radius;
    }
    
    public override double GetArea() => Math.PI * Radius * Radius;
    public override double GetPerimeter() => 2 * Math.PI * Radius;
    
    public override void Draw()
    {
        Console.WriteLine($"绘制一个{Color}的圆,半径:{Radius}");
    }
}

// 矩形
class Rectangle : Shape
{
    public double Width { get; set; }
    public double Height { get; set; }
    
    public Rectangle(double width, double height, string color) : base(color)
    {
        Width = width;
        Height = height;
    }
    
    public override double GetArea() => Width * Height;
    public override double GetPerimeter() => 2 * (Width + Height);
    
    public override void Draw()
    {
        Console.WriteLine($"绘制一个{Color}的矩形,{Width}x{Height}");
    }
}

// 三角形
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, string color) : base(color)
    {
        SideA = a;
        SideB = b;
        SideC = c;
    }
    
    public override double GetArea()
    {
        // 海伦公式
        double s = (SideA + SideB + SideC) / 2;
        return Math.Sqrt(s * (s - SideA) * (s - SideB) * (s - SideC));
    }
    
    public override double GetPerimeter() => SideA + SideB + SideC;
    
    public override void Draw()
    {
        Console.WriteLine($"绘制一个{Color}的三角形,边长:{SideA},{SideB},{SideC}");
    }
}

// 正方形(继承自矩形)
class Square : Rectangle
{
    public Square(double side, string color) : base(side, side, color)
    {
    }
    
    public override void Draw()
    {
        Console.WriteLine($"绘制一个{Color}的正方形,边长:{Width}");
    }
}

// 绘图程序
class DrawingProgram
{
    private List<Shape> shapes = new List<Shape>();
    
    public void AddShape(Shape shape)
    {
        shapes.Add(shape);
        Console.WriteLine($"已添加{shape.GetType().Name}");
    }
    
    public void DrawAll()
    {
        Console.WriteLine("\n=== 绘制所有图形 ===");
        foreach (Shape shape in shapes)
        {
            shape.Draw();
        }
    }
    
    public void ShowAllInfo()
    {
        Console.WriteLine("\n=== 所有图形信息 ===");
        foreach (Shape shape in shapes)
        {
            shape.ShowInfo();
            Console.WriteLine();
        }
    }
    
    public double GetTotalArea()
    {
        double total = 0;
        foreach (Shape shape in shapes)
        {
            total += shape.GetArea();
        }
        return total;
    }
    
    public void FilterByColor(string color)
    {
        Console.WriteLine($"\n=== {color}色的图形 ===");
        foreach (Shape shape in shapes)
        {
            if (shape.Color.Equals(color, StringComparison.OrdinalIgnoreCase))
            {
                shape.Draw();
            }
        }
    }
}

class Program
{
    static void Main()
    {
        DrawingProgram program = new DrawingProgram();
        
        // 添加各种形状
        program.AddShape(new Circle(5, "红色"));
        program.AddShape(new Rectangle(10, 6, "蓝色"));
        program.AddShape(new Triangle(3, 4, 5, "绿色"));
        program.AddShape(new Square(7, "黄色"));
        program.AddShape(new Circle(3, "红色"));
        
        // 绘制所有
        program.DrawAll();
        
        // 显示信息
        program.ShowAllInfo();
        
        // 总面积
        Console.WriteLine($"\n所有图形总面积:{program.GetTotalArea():F2}");
        
        // 按颜色筛选
        program.FilterByColor("红色");
    }
}

示例2:员工工资管理系统

csharp

复制代码
using System;
using System.Collections.Generic;

// 员工抽象类
abstract class Employee
{
    public string Id { get; set; }
    public string Name { get; set; }
    public DateTime HireDate { get; set; }
    
    protected Employee(string id, string name, DateTime hireDate)
    {
        Id = id;
        Name = name;
        HireDate = hireDate;
    }
    
    // 抽象方法:计算工资
    public abstract double CalculateSalary();
    
    // 虚方法:计算工龄工资
    public virtual double CalculateSeniorityBonus()
    {
        int years = DateTime.Now.Year - HireDate.Year;
        if (DateTime.Now.DayOfYear < HireDate.DayOfYear) years--;
        return years * 100;  // 每年100元工龄工资
    }
    
    // 显示信息
    public virtual void ShowInfo()
    {
        Console.WriteLine($"ID:{Id},姓名:{Name},入职日期:{HireDate:yyyy-MM-dd}");
        Console.WriteLine($"工资:{CalculateSalary():C},工龄工资:{CalculateSeniorityBonus():C}");
    }
}

// 全职员工
class FullTimeEmployee : Employee
{
    public double BaseSalary { get; set; }
    public double Bonus { get; set; }
    
    public FullTimeEmployee(string id, string name, DateTime hireDate, 
                            double baseSalary, double bonus) 
        : base(id, name, hireDate)
    {
        BaseSalary = baseSalary;
        Bonus = bonus;
    }
    
    public override double CalculateSalary()
    {
        return BaseSalary + Bonus + CalculateSeniorityBonus();
    }
    
    public override void ShowInfo()
    {
        Console.WriteLine($"\n[全职员工]");
        base.ShowInfo();
        Console.WriteLine($"基本工资:{BaseSalary:C},奖金:{Bonus:C}");
    }
}

// 兼职员工(按时计薪)
class PartTimeEmployee : Employee
{
    public double HourlyRate { get; set; }
    public double HoursWorked { get; set; }
    
    public PartTimeEmployee(string id, string name, DateTime hireDate,
                            double hourlyRate, double hoursWorked)
        : base(id, name, hireDate)
    {
        HourlyRate = hourlyRate;
        HoursWorked = hoursWorked;
    }
    
    public override double CalculateSalary()
    {
        return HourlyRate * HoursWorked + CalculateSeniorityBonus();
    }
    
    public override void ShowInfo()
    {
        Console.WriteLine($"\n[兼职员工]");
        base.ShowInfo();
        Console.WriteLine($"时薪:{HourlyRate:C},工时:{HoursWorked}小时");
    }
}

// 销售员工(底薪+提成)
class SalesEmployee : Employee
{
    public double BaseSalary { get; set; }
    public double SalesAmount { get; set; }
    public double CommissionRate { get; set; }  // 提成比例
    
    public SalesEmployee(string id, string name, DateTime hireDate,
                         double baseSalary, double commissionRate)
        : base(id, name, hireDate)
    {
        BaseSalary = baseSalary;
        CommissionRate = commissionRate;
        SalesAmount = 0;
    }
    
    public void RecordSales(double amount)
    {
        SalesAmount += amount;
        Console.WriteLine($"{Name} 销售业绩 +{amount:C},总业绩:{SalesAmount:C}");
    }
    
    public override double CalculateSalary()
    {
        return BaseSalary + SalesAmount * CommissionRate + CalculateSeniorityBonus();
    }
    
    public override void ShowInfo()
    {
        Console.WriteLine($"\n[销售员工]");
        base.ShowInfo();
        Console.WriteLine($"底薪:{BaseSalary:C},提成比例:{CommissionRate:P0}");
        Console.WriteLine($"当前业绩:{SalesAmount:C},业绩提成:{SalesAmount * CommissionRate:C}");
    }
}

// 实习生(没有工龄工资)
class Intern : Employee
{
    public double Stipend { get; set; }  // 津贴
    
    public Intern(string id, string name, DateTime hireDate, double stipend)
        : base(id, name, hireDate)
    {
        Stipend = stipend;
    }
    
    public override double CalculateSalary()
    {
        return Stipend;  // 实习生没有工龄工资
    }
    
    // 重写工龄工资方法
    public override double CalculateSeniorityBonus()
    {
        return 0;  // 实习生没有工龄工资
    }
    
    public override void ShowInfo()
    {
        Console.WriteLine($"\n[实习生]");
        base.ShowInfo();
        Console.WriteLine($"津贴:{Stipend:C}");
    }
}

// 工资管理系统
class PayrollSystem
{
    private List<Employee> employees = new List<Employee>();
    
    public void AddEmployee(Employee emp)
    {
        employees.Add(emp);
        Console.WriteLine($"已添加员工:{emp.Name}");
    }
    
    public void ProcessPayroll()
    {
        Console.WriteLine("\n" + new string('=', 50));
        Console.WriteLine("开始处理工资...");
        Console.WriteLine(new string('=', 50));
        
        double totalSalary = 0;
        
        foreach (Employee emp in employees)
        {
            emp.ShowInfo();
            totalSalary += emp.CalculateSalary();
            Console.WriteLine();
        }
        
        Console.WriteLine(new string('-', 50));
        Console.WriteLine($"总工资支出:{totalSalary:C}");
        Console.WriteLine(new string('=', 50));
    }
    
    public void ShowEmployeeByType<T>() where T : Employee
    {
        Console.WriteLine($"\n=== {typeof(T).Name} 列表 ===");
        foreach (Employee emp in employees)
        {
            if (emp is T)
            {
                Console.WriteLine($"{emp.Id} - {emp.Name}");
            }
        }
    }
    
    public double GetTotalSalary()
    {
        double total = 0;
        foreach (Employee emp in employees)
        {
            total += emp.CalculateSalary();
        }
        return total;
    }
}

class Program
{
    static void Main()
    {
        PayrollSystem payroll = new PayrollSystem();
        
        // 添加各种类型员工
        payroll.AddEmployee(new FullTimeEmployee("E001", "张三", 
            new DateTime(2020, 1, 15), 8000, 2000));
        
        payroll.AddEmployee(new PartTimeEmployee("E002", "李四", 
            new DateTime(2023, 6, 1), 50, 80));
        
        SalesEmployee sales = new SalesEmployee("E003", "王五", 
            new DateTime(2021, 3, 10), 5000, 0.05);
        sales.RecordSales(100000);
        payroll.AddEmployee(sales);
        
        payroll.AddEmployee(new Intern("E004", "赵六", 
            new DateTime(2024, 1, 1), 2000));
        
        // 处理工资
        payroll.ProcessPayroll();
        
        // 按类型统计
        payroll.ShowEmployeeByType<FullTimeEmployee>();
        payroll.ShowEmployeeByType<SalesEmployee>();
    }
}

示例3:简单游戏角色系统

csharp

复制代码
using System;

// 角色基类
abstract class Character
{
    public string Name { get; set; }
    public int Health { get; protected set; }
    public int MaxHealth { get; protected set; }
    public int Level { get; protected set; }
    public bool IsAlive => Health > 0;
    
    protected Character(string name, int maxHealth)
    {
        Name = name;
        MaxHealth = maxHealth;
        Health = maxHealth;
        Level = 1;
    }
    
    // 抽象攻击方法
    public abstract int Attack();
    
    // 受伤害方法
    public virtual void TakeDamage(int damage)
    {
        Health -= damage;
        if (Health < 0) Health = 0;
        Console.WriteLine($"{Name} 受到 {damage} 点伤害,剩余生命:{Health}/{MaxHealth}");
        
        if (!IsAlive)
        {
            Console.WriteLine($"{Name} 已经倒下!");
        }
    }
    
    // 治疗
    public virtual void Heal(int amount)
    {
        Health += amount;
        if (Health > MaxHealth) Health = MaxHealth;
        Console.WriteLine($"{Name} 恢复了 {amount} 点生命,当前生命:{Health}/{MaxHealth}");
    }
    
    // 升级
    public virtual void LevelUp()
    {
        Level++;
        MaxHealth += 20;
        Health = MaxHealth;
        Console.WriteLine($"{Name} 升级到 {Level} 级!最大生命增加到 {MaxHealth}");
    }
    
    // 显示状态
    public virtual void ShowStatus()
    {
        Console.WriteLine($"[{GetType().Name}] {Name} | Lv.{Level} | HP: {Health}/{MaxHealth}");
    }
}

// 战士
class Warrior : Character
{
    public int Strength { get; set; }
    
    public Warrior(string name) : base(name, 120)
    {
        Strength = 15;
    }
    
    public override int Attack()
    {
        int damage = Strength + Level * 2;
        Console.WriteLine($"{Name} 使用【猛击】,造成 {damage} 点伤害!");
        return damage;
    }
    
    // 战士特有技能:盾墙(减伤)
    public void ShieldWall()
    {
        Console.WriteLine($"{Name} 举盾防御,接下来受到的伤害减半!");
        // 简化实现,实际可以用 Buff 系统
    }
    
    public override void LevelUp()
    {
        base.LevelUp();
        Strength += 5;
        Console.WriteLine($"力量提升到 {Strength}");
    }
}

// 法师
class Mage : Character
{
    public int Intelligence { get; set; }
    public int Mana { get; set; }
    
    public Mage(string name) : base(name, 80)
    {
        Intelligence = 18;
        Mana = 100;
    }
    
    public override int Attack()
    {
        int damage = Intelligence + Level * 3;
        if (Mana >= 20)
        {
            Mana -= 20;
            Console.WriteLine($"{Name} 施放【火球术】,造成 {damage} 点伤害!(剩余法力:{Mana})");
            return damage;
        }
        else
        {
            Console.WriteLine($"{Name} 法力不足,使用法杖敲击,造成 5 点伤害!");
            return 5;
        }
    }
    
    // 法师特有技能:冥想(回复法力)
    public void Meditate()
    {
        Mana += 30;
        if (Mana > 100) Mana = 100;
        Console.WriteLine($"{Name} 进入冥想,回复30点法力,当前法力:{Mana}");
    }
    
    public override void LevelUp()
    {
        base.LevelUp();
        Intelligence += 4;
        Mana = 100;
        Console.WriteLine($"智力提升到 {Intelligence},法力回满");
    }
}

// 弓箭手
class Archer : Character
{
    public int Dexterity { get; set; }
    public int Arrows { get; set; }
    
    public Archer(string name) : base(name, 100)
    {
        Dexterity = 14;
        Arrows = 30;
    }
    
    public override int Attack()
    {
        int damage = Dexterity + Level * 2;
        if (Arrows > 0)
        {
            Arrows--;
            Console.WriteLine($"{Name} 射出一箭,造成 {damage} 点伤害!(剩余箭矢:{Arrows})");
            return damage;
        }
        else
        {
            Console.WriteLine($"{Name} 箭矢用尽,无法攻击!");
            return 0;
        }
    }
    
    // 弓箭手特有技能:装填箭矢
    public void Reload()
    {
        Arrows += 10;
        Console.WriteLine($"{Name} 装填了10支箭矢,当前箭矢:{Arrows}");
    }
    
    public override void LevelUp()
    {
        base.LevelUp();
        Dexterity += 4;
        Arrows += 5;
        Console.WriteLine($"敏捷提升到 {Dexterity},箭矢上限+5");
    }
}

// 战斗系统
class BattleSystem
{
    public void Fight(Character attacker, Character defender)
    {
        Console.WriteLine($"\n>>> {attacker.Name} 攻击 {defender.Name}!");
        int damage = attacker.Attack();
        defender.TakeDamage(damage);
    }
    
    public void TeamBattle(Character[] team1, Character[] team2)
    {
        Console.WriteLine("\n" + new string('=', 50));
        Console.WriteLine("团队战斗开始!");
        Console.WriteLine(new string('=', 50));
        
        Random random = new Random();
        
        while (IsTeamAlive(team1) && IsTeamAlive(team2))
        {
            // 随机选择攻击者和防御者
            Character attacker = GetRandomAlive(team1, team2, random);
            Character defender = GetRandomAlive(team2, team1, random);
            
            if (attacker != null && defender != null)
            {
                Fight(attacker, defender);
            }
            
            // 每轮结束后显示状态
            Console.WriteLine("\n当前状态:");
            ShowTeamStatus(team1);
            ShowTeamStatus(team2);
            Console.WriteLine(new string('-', 30));
            
            System.Threading.Thread.Sleep(1000);
        }
        
        // 宣布胜者
        if (IsTeamAlive(team1))
            Console.WriteLine("\n🏆 队伍1获胜!");
        else
            Console.WriteLine("\n🏆 队伍2获胜!");
    }
    
    private bool IsTeamAlive(Character[] team)
    {
        foreach (Character c in team)
        {
            if (c.IsAlive) return true;
        }
        return false;
    }
    
    private Character GetRandomAlive(Character[] attackers, Character[] defenders, Random random)
    {
        List<Character> alive = new List<Character>();
        foreach (Character c in attackers)
        {
            if (c.IsAlive) alive.Add(c);
        }
        
        if (alive.Count == 0) return null;
        return alive[random.Next(alive.Count)];
    }
    
    private void ShowTeamStatus(Character[] team)
    {
        foreach (Character c in team)
        {
            c.ShowStatus();
        }
    }
}

class Program
{
    static void Main()
    {
        // 创建角色
        Warrior warrior = new Warrior("战神");
        Mage mage = new Mage("大法师");
        Archer archer = new Archer("神射手");
        
        // 显示初始状态
        Console.WriteLine("=== 角色创建 ===");
        warrior.ShowStatus();
        mage.ShowStatus();
        archer.ShowStatus();
        
        Console.WriteLine("\n=== 升级测试 ===");
        warrior.LevelUp();
        mage.LevelUp();
        
        Console.WriteLine("\n=== 技能测试 ===");
        warrior.ShieldWall();
        mage.Meditate();
        archer.Reload();
        
        Console.WriteLine("\n=== 战斗测试 ===");
        BattleSystem battle = new BattleSystem();
        battle.Fight(warrior, mage);
        battle.Fight(mage, warrior);
        
        // 团队战斗
        Console.WriteLine("\n=== 团队战斗测试 ===");
        Character[] team1 = new Character[] 
        { 
            new Warrior("战士A"), 
            new Mage("法师A") 
        };
        Character[] team2 = new Character[] 
        { 
            new Archer("弓箭手B"), 
            new Warrior("战士B") 
        };
        
        battle.TeamBattle(team1, team2);
    }
}

8.8 常见错误与陷阱

错误1:忘记在子类中实现抽象方法

csharp

复制代码
abstract class Base
{
    public abstract void MustImplement();
}

class Derived : Base
{
    // 没有实现 MustImplement()  // ❌ 编译错误
}

错误2:sealed 类被继承

csharp

复制代码
sealed class Final { }

class Derived : Final  // ❌ 错误
{
}

错误3:base 调用错误

csharp

复制代码
class Parent
{
    public Parent(int x) { }
}

class Child : Parent
{
    // 没有调用父类构造函数  // ❌ 错误
    public Child() { }
}

错误4:is 和 as 使用混乱

csharp

复制代码
object obj = "123";
int num = obj as int;  // ❌ int 是值类型,不能用于 as
// 值类型应使用 is + 强制转换
if (obj is int)
{
    int num = (int)obj;
}

错误5:构造函数中调用虚方法

csharp

复制代码
class Base
{
    public Base()
    {
        VirtualMethod();  // ⚠️ 危险!可能调用子类未完全初始化的方法
    }
    
    public virtual void VirtualMethod() { }
}

8.9 本章总结

核心知识点导图

text

复制代码
面向对象编程(进阶)
├── 继承
│   ├── 语法:class 子类 : 父类
│   ├── base 关键字:访问父类成员
│   ├── 构造函数调用链
│   └── 访问修饰符的影响
│
├── 多态
│   ├── virtual + override
│   ├── 通过父类引用调用子类方法
│   └── 提高代码扩展性
│
├── 抽象类与抽象方法
│   ├── abstract 关键字
│   ├── 不能实例化
│   └── 子类必须实现抽象方法
│
├── 密封类与方法
│   ├── sealed class:不能被继承
│   └── sealed override:阻止进一步重写
│
└── 类型转换
    ├── is:类型检查
    ├── as:安全转换(引用类型)
    └── 强制转换:(类型)对象

继承 vs 接口(预告)

特性 继承类 实现接口(第九章)
可以继承多个
可以提供实现 ❌(默认实现除外)
可以包含字段
可以包含构造函数
相关推荐
m0_748839498 小时前
R包grafify:简单操作实现高效统计绘图
开发语言·r语言
Evand J8 小时前
【课题推荐与代码介绍】卡尔曼滤波器正反向估计算法原理与MATLAB实现
开发语言·算法·matlab
奋斗的小方8 小时前
Java基础篇09:项目实战
java·开发语言
froginwe118 小时前
Vue.js 监听属性
开发语言
c++逐梦人8 小时前
五种IO模型与⾮阻塞IO
开发语言·网络
翎沣8 小时前
C++面向对象三大特性
开发语言·c++
驭渊的小故事8 小时前
java中的进程的详细解析
java·开发语言
烟雨江南aabb8 小时前
Python第六弹:python爬虫篇:什么是爬虫
开发语言·爬虫·python
沐知全栈开发8 小时前
Servlet 文件上传详解
开发语言