🧩 C# 抽象类详解
抽象类(abstract class)是 面向对象编程(OOP) 中的重要概念,
用于定义一个 不能直接实例化 的类,
作为其他类的「蓝图」存在。它通常用来表示一类对象的共同特征与行为约束。
🧱 一、定义与基本语法
public abstract class Animal
{
// 抽象方法(没有实现)——必须由子类实现
public abstract void Speak();
// 虚方法(可以被子类重写,也可以直接使用)
public virtual void Move()
{
Console.WriteLine("Animal is moving...");
}
// 普通方法(有固定实现,子类不能重写)
public void Eat()
{
Console.WriteLine("Animal is eating...");
}
}
-
abstract关键字修饰类或方法。 -
抽象类 不能被实例化。
-
抽象类中可以包含:
- 抽象方法(必须由子类实现)
- 普通方法(可直接继承使用)
- 属性、字段、构造函数等。
🐕 二、继承与实现
public class Dog : Animal
{
// 实现抽象方法
public override void Speak()
{
Console.WriteLine("Woof!");
}
// 重写虚方法
public override void Move()
{
Console.WriteLine("Dog is running...");
}
}
使用:
Animal animal = new Dog();
animal.Speak(); // 输出:Woof!
animal.Move(); // 输出:Dog is running...
animal.Eat(); // 输出:Animal is eating...✅ 说明:
- 通过 向上转型(Upcasting),可以用
Animal类型引用具体子类对象。 - 实现了多态行为:相同的方法,不同的实现。
| 方法类型 | 关键字 | 是否有实现 | 是否必须重写 | 是否可选重写 | 示例 |
|---|---|---|---|---|---|
| 抽象方法 | abstract | ❌ 否 | ✅ 必须 | ❌ 不可选 | public abstract void Speak(); |
| 虚方法 | virtual | ✅ 有默认实现 | ❌ 不强制 | ✅ 可选 | public virtual void Move() { } |
| 普通方法 | 无 | ✅ 有实现 | ❌ 不允许 | ❌ 不允许 | public void Eat() { } |
⚖️ 三、抽象类 vs 接口(interface)
| 对比项 | 抽象类 | 接口 |
|---|---|---|
| 是否能包含字段 | ✅ 可以 | ❌ 不可以 |
| 是否能包含实现 | ✅ 可以(普通方法) | ✅ 从 C# 8 起可以有默认实现 |
| 是否支持多继承 | ❌ 不支持 | ✅ 支持多接口实现 |
| 构造函数 | ✅ 可以 | ❌ 不可以 |
| 用途 | 表示类之间的继承关系 | 表示行为契约 |
💬 一般来说,当多个类有相似逻辑时,用抽象类; 当只需定义“规范”时,用接口。
🧠 四、常见面试题
❓ 1. 抽象类可以包含构造函数吗?
✅ 可以。用于初始化共有字段。
但仍不能直接通过 new 实例化该类。
public abstract class Shape
{
protected Shape(string name)
{
Name = name;
}
public string Name { get; }
}❓ 2. 抽象类能被 sealed 修饰吗?
❌ 不行。
sealed 表示类不能被继承,而抽象类的意义正是被继承。
❓ 3. 抽象方法可以是 static 吗?
❌ 不可以。 静态方法不能被重写,而抽象方法必须由子类重写。
❓ 4. 抽象类能实现接口吗?
✅ 可以,且子类会继承接口约定。
public interface IMovable { void Move(); }
public abstract class Vehicle : IMovable
{
public abstract void Move();
}🧩 五、实践示例:
模板方法模式(Template Method)
抽象类常用于「模板方法模式」:
public abstract class DataProcessor
{
public void Process()
{
Load();
Transform();
Save();
}
protected abstract void Load();
protected abstract void Transform();
protected abstract void Save();
}
public class CsvProcessor : DataProcessor
{
protected override void Load() => Console.WriteLine("Loading CSV...");
protected override void Transform() => Console.WriteLine("Transforming CSV data...");
protected override void Save() => Console.WriteLine("Saving results...");
}使用:
DataProcessor processor = new CsvProcessor();
processor.Process();👉 输出:
Loading CSV...
Transforming CSV data...
Saving results...这是一种典型的 OOP 设计模式: 父类定义流程,子类定义细节。
EF实体继承设计模式
✅ 一、典型示例
public abstract class BaseEntity
{
public int Id { get; set; }
public DateTime CreatedTime { get; set; } = DateTime.UtcNow;
public DateTime? UpdatedTime { get; set; }
public bool IsDeleted { get; set; } = false;
}然后其他实体继承这个抽象类:
public class User : BaseEntity
{
public string Name { get; set; } = default!;
public string Email { get; set; } = default!;
}这样所有表都会自动带上这些公共字段。 EF 会在迁移时自动识别并生成这些列。
⚠️ 六、注意事项
- 抽象类中至少要有一个抽象成员,否则通常可改为普通类。
- 抽象方法 不能有实现体。
- 子类必须使用
override实现抽象方法。 - 如果子类没有实现所有抽象方法,它也必须声明为
abstract。
🔍 七、小结
| 特性 | 说明 |
|---|---|
| 关键字 | abstract |
| 作用 | 定义模板类,约束子类实现 |
| 能否实例化 | ❌ 不可 |
| 是否可继承 | ✅ 必须被继承 |
| 是否可包含实现 | ✅ 可以 |
| 常见应用 | 模板方法模式、基类设计、框架扩展点 |
🧭 推荐阅读
💬 “抽象类的核心不是省代码,而是设计思维: 把变化的部分留给子类,让稳定的部分在父类中复用。”