C# 抽象类
1. 什么是抽象类?
用 abstract 关键字修饰的类,代表一个不完整的基类,用于被其他类继承。它不能实例化,但可以包含抽象成员(没有实现)和具体成员(有实现)。
本质定位:“半成品蓝图” —— 定义统一规范,同时提供可复用的代码。
2. 语法与代码示例
抽象类定义
public abstract class Animal
{
public string Name { get; set; }
// 构造函数(子类实例化时调用)
public Animal(string name) => Name = name;
// 普通方法(子类直接继承)
public void Eat() => Console.WriteLine($"{Name} is eating.");
// 抽象方法(强制子类重写)
public abstract void MakeSound();
}
子类实现
public class Dog : Animal
{
public Dog(string name) : base(name) { }
public override void MakeSound() => Console.WriteLine($"{Name} says: Wang Wang!");
}
public class Cat : Animal
{
public Cat(string name) : base(name) { }
public override void MakeSound() => Console.WriteLine($"{Name} says: Miao Miao!");
}
使用(多态)
Animal myDog = new Dog("Buddy");
myDog.Eat(); // 继承自抽象类
myDog.MakeSound(); // 子类重写的实现
3. 抽象方法 vs 虚方法(abstract vs virtual)
| 对比项 | 抽象方法 (abstract) | 虚方法 (virtual) |
|---|---|---|
| 是否有默认实现 | 没有,只有声明 | 有,提供默认代码 |
| 子类是否必须重写 | 必须重写(除非子类也是抽象类) | 可选重写(可不重写使用基类实现) |
| 所在类要求 | 只能在抽象类中 | 可以放在普通类或抽象类中 |
4. 抽象类 vs 接口(关键对比)
| 维度 | 抽象类 | 接口 (Interface) |
|---|---|---|
| 核心哲学 | “是什么”(Is-A) — 猫是动物 | “能做什么”(Can-Do) — 飞机能飞,鸟也能飞 |
| 实例字段/状态 | 可以拥有字段、属性(含私有字段) | 不能拥有实例字段(C# 8 前),只能有属性签名 |
| 构造函数 | 可以有构造函数 | 不能有构造函数 |
| 访问修饰符 | 成员可为 private, protected, public 等 | 默认 public(早期版本不允许修饰符) |
| 多继承 | 一个类只能继承一个抽象类 | 一个类可以继承多个接口 |
| 版本演进 | 给抽象类加新方法,需提供虚实现,否则子类报错 | 给接口加新方法,实现类不会强制报错(需默认实现) |
5. 什么时候用抽象类?
- 模板方法模式:固定算法骨架,部分步骤由子类实现(例如饮料冲泡流程)。
- 代码复用:多个子类有大量公共字段、属性、方法,抽取到抽象类避免重复。
- 强制规范:需要子类必须实现某些行为(如
MakeSound())。
6. 避坑小贴士
- 密封类(sealed)不能是抽象类 — 两者互斥。
- 抽象类可以没有任何抽象方法(纯为了禁止实例化),但通常不推荐,更建议用静态类。
- 如果子类不想实现全部抽象方法,子类也必须标记为
abstract,把责任继续传递。 - 抽象方法不能是
private,否则子类无法重写。
7. 常见面试题(附答题思路)
1. 抽象类能否实例化?为什么?
不能。抽象类是不完整的(可能包含未实现的方法),如果允许实例化,调用抽象方法时无法执行。编译器会阻止 new 操作。
2. 抽象类可以有构造函数吗?如果可以,它有什么用?
可以。虽然不能直接 new,但子类实例化时会通过 base() 调用基类构造函数,用于初始化抽象类中的字段或执行必要的设置。
3. 抽象方法和虚方法有什么区别?什么时候用哪个?
抽象方法没有实现,必须由子类重写,适用于“强制子类提供特定行为”。
虚方法有默认实现,子类可以选择重写或不重写,适用于“提供通用行为,允许子类扩展”。
4. 抽象类和接口的区别是什么?你如何选择?
核心区别:抽象类用于“是什么”的层次关系(is-a),接口用于“能做什么”的能力(can-do)。
选择建议:如果有公共字段/状态需要共享,或者需要提供默认实现,用抽象类;如果只定义行为契约,且需要多继承(实现多个接口),用接口。
5. 能否在抽象类中定义静态成员?
可以。静态成员属于类本身,与实例无关,抽象类可以包含静态字段、方法等,子类可以直接通过类名访问(如果是 public)。
6. 如果一个类继承自抽象类,但没有实现所有抽象方法,会发生什么?
编译错误。除非子类也声明为 abstract,否则必须 override 所有抽象成员。
7. C# 中一个类可以继承多个抽象类吗?
不能。C# 不支持类的多重继承(无论是否抽象),但可以通过接口实现多重行为组合。
8. 抽象类中的抽象方法能否标记为 virtual?
不能。抽象方法隐含了“必须重写”的含义,它本身就是虚的,但不需要额外标记 virtual。如果标记会编译错误。
8. 延伸 · 设计模式中的抽象类
- 模板方法模式:抽象类定义算法骨架,具体步骤延迟到子类。
- 工厂方法模式:抽象类声明工厂方法,子类决定创建哪个具体对象。
- 策略模式:有时用抽象类代替接口来提供公共基础功能。

浙公网安备 33010602011771号