跳到主要内容

C# 继承

本章将详细介绍 C# 中的继承机制,包括类继承、构造函数调用、方法重写等。

继承的基本概念

什么是继承?

继承是面向对象编程的核心特性之一,它允许我们基于已有的类创建新类,新类可以继承已有类的属性和方法。

┌─────────────────────┐
│ 基类 (Parent) │
│ ────────────────── │
│ + Name │
│ + Age │
│ + Eat() │
│ + Sleep() │
└─────────────────────┘

│ 继承
┌─────────────────────┐
│ 派生类 (Child) │
│ ────────────────── │
│ + Name │ ← 继承
│ + Age │ ← 继承
│ + Eat() │ ← 继承
│ + Sleep() │ ← 继承
│ + Study() │ ← 新增
│ + Exam() │ ← 新增
└─────────────────────┘

继承的实现

单继承

// 基类
class Animal
{
public string Name { get; set; }
public int Age { get; set; }

public Animal(string name, int age)
{
Name = name;
Age = age;
}

public void Eat()
{
Console.WriteLine($"{Name} 正在吃东西");
}

public void Sleep()
{
Console.WriteLine($"{Name} 正在睡觉");
}
}

// 派生类
class Dog : Animal
{
public string Breed { get; set; }

public Dog(string name, int age, string breed)
: base(name, age) // 调用基类构造函数
{
Breed = breed;
}

// 新增方法
public void Bark()
{
Console.WriteLine($"{Name} 正在汪汪叫");
}

// 重写基类方法
public override void Eat()
{
Console.WriteLine($"{Name} 正在吃狗粮");
}
}

// 使用
Dog dog = new Dog("旺财", 3, "金毛");
dog.Eat(); // 调用重写后的方法
dog.Sleep(); // 继承自基类的方法
dog.Bark(); // 派生类自己的方法

多层继承

// 爷爷类
class LivingThing
{
public void Breathe()
{
Console.WriteLine("呼吸");
}
}

// 父类
class Animal : LivingThing
{
public void Eat()
{
Console.WriteLine("吃东西");
}
}

// 子类
class Dog : Animal
{
public void Bark()
{
Console.WriteLine("汪汪叫");
}
}

// 使用
Dog dog = new Dog();
dog.Breathe(); // 继承自爷爷类
dog.Eat(); // 继承自父类
dog.Bark(); // 自己独有的

多继承的限制

C# 不支持直接多继承(一个类继承多个类),但可以通过接口实现多继承的效果:

// 不允许这样的继承
// class Student : Person, Teacher // 错误!

// 可以实现多个接口
class Student : Person, IStudy, IWork
{
public void Study() { }
public void Work() { }
}

基类构造函数

使用 base 关键字

class Person
{
public string Name { get; set; }
public int Age { get; set; }

public Person(string name, int age)
{
Name = name;
Age = age;
}
}

class Student : Person
{
public string School { get; set; }

// 显式调用基类构造函数
public Student(string name, int age, string school)
: base(name, age)
{
School = school;
}
}

默认基类构造函数

class Base
{
public Base()
{
Console.WriteLine("Base 构造函数");
}
}

class Derived : Base
{
public Derived()
{
// 隐式调用 base()
Console.WriteLine("Derived 构造函数");
}
}

new Derived();
// 输出:
// Base 构造函数
// Derived 构造函数

方法重写

virtual 和 override

class Shape
{
// 虚方法:可以在派生类中重写
public virtual double Area()
{
return 0;
}

public virtual void Draw()
{
Console.WriteLine("绘制图形");
}
}

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 Area() => Width * Height;

public override void Draw()
{
Console.WriteLine($"绘制矩形: {Width}x{Height}");
}
}

class Circle : Shape
{
public double Radius { get; set; }

public Circle(double radius)
{
Radius = radius;
}

public override double Area() => Math.PI * Radius * Radius;

public override void Draw()
{
Console.WriteLine($"绘制圆: 半径={Radius}");
}
}

new 关键字(隐藏方法)

class Base
{
public void Method()
{
Console.WriteLine("Base Method");
}
}

class Derived : Base
{
// 使用 new 隐藏基类方法
public new void Method()
{
Console.WriteLine("Derived Method");
}
}

Base b = new Derived();
b.Method(); // 输出: Base Method(调用的是基类方法)

Derived d = new Derived();
d.Method(); // 输出: Derived Method

base 关键字的使用

class Animal
{
public virtual void Speak()
{
Console.WriteLine("动物叫声");
}
}

class Cat : Animal
{
public override void Speak()
{
// 调用基类的实现
base.Speak();
// 然后添加自己的逻辑
Console.WriteLine("喵喵喵");
}
}

Cat cat = new Cat();
cat.Speak();
/*
输出:
动物叫声
喵喵喵
*/

抽象类

abstract 关键字

// 抽象类:不能实例化
abstract class Animal
{
public string Name { get; set; }

// 抽象方法:没有实现,必须在派生类中重写
public abstract void Speak();

// 抽象属性
public abstract int Age { get; set; }

// 抽象类可以有具体方法
public void Sleep()
{
Console.WriteLine($"{Name} 正在睡觉");
}
}

class Dog : Animal
{
public override int Age { get; set; }

public override void Speak()
{
Console.WriteLine($"{Name} 说: 汪汪");
}
}

// 使用
Animal animal = new Dog { Name = "旺财", Age = 3 };
animal.Speak();
animal.Sleep();
// animal 不能直接 new Animal()

抽象类示例

abstract class Vehicle
{
public string Brand { get; set; }
public string Model { get; set; }

// 抽象方法
public abstract void Start();
public abstract void Stop();

// 具体方法
public void DisplayInfo()
{
Console.WriteLine($"{Brand} - {Model}");
}
}

class Car : Vehicle
{
public override void Start()
{
Console.WriteLine("汽车启动");
}

public override void Stop()
{
Console.WriteLine("汽车停止");
}
}

class Motorcycle : Vehicle
{
public override void Start()
{
Console.WriteLine("摩托车启动");
}

public override void Stop()
{
Console.WriteLine("摩托车停止");
}
}

访问基类成员

protected 修饰符

class Person
{
protected string name = "张三"; // 受保护成员
private int age = 20; // 私有成员
}

class Student : Person
{
public void Print()
{
Console.WriteLine(name); // 可以访问 protected 成员
// Console.WriteLine(age); // 错误!不能访问 private 成员
}
}

继承与组合

组合 vs 继承

// 继承:is-a 关系
class Animal { }
class Dog : Animal { } // Dog is an Animal

// 组合:has-a 关系
class Engine { }
class Car
{
private Engine _engine; // Car has an Engine

public Car(Engine engine)
{
_engine = engine;
}
}

优先使用组合

// 不推荐的继承用法
class CoffeeMachine
{
public void MakeCoffee() { }
}

class AdvancedCoffeeMachine : CoffeeMachine // 不恰当
{
public void MakeTea() { }
}

// 更好的方式:组合
class CoffeeMachine
{
public void MakeCoffee() { }
}

class AdvancedAppliance
{
private CoffeeMachine _coffeeMachine = new CoffeeMachine();

public void MakeCoffee() => _coffeeMachine.MakeCoffee();
public void MakeTea() { /* 新的功能 */ }
}

多态

向上转型

Animal animal = new Dog("旺财", 3);
animal.Eat(); // 调用 Dog 的方法(如果重写了)
// animal.Bark(); // 错误!Animal 类型看不到 Dog 的方法

向下转型

Animal animal = new Dog("旺财", 3);

// 安全检查
if (animal is Dog dog)
{
dog.Bark(); // 可以使用 Dog 的方法
}

// 使用 as
Dog dog2 = animal as Dog;
if (dog2 != null)
{
dog2.Bark();
}

多态的应用

class Payment
{
public virtual void Process()
{
Console.WriteLine("处理支付");
}
}

class WeChatPay : Payment
{
public override void Process()
{
Console.WriteLine("微信支付");
}
}

class AliPay : Payment
{
public override void Process()
{
Console.WriteLine("支付宝");
}
}

// 多态调用
Payment[] payments = {
new WeChatPay(),
new AliPay(),
new Payment()
};

foreach (var p in payments)
{
p.Process(); // 调用各自的重写方法
}

小结

  1. 单继承:C# 只允许单继承,可以用接口弥补
  2. base 关键字:调用基类构造函数和方法
  3. virtual/override:实现运行时多态
  4. new 关键字:隐藏基类方法
  5. 抽象类:包含抽象成员的类,不能实例化
  6. 组合优先:优先使用组合而非继承

练习

  1. 创建一个基类 Shape,派生出 CircleRectangleTriangle
  2. 使用抽象类实现一个员工管理系统
  3. 实现一个多态的支付系统(支持不同支付方式)
  4. 理解组合和继承的使用场景