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. 延伸 · 设计模式中的抽象类

  • 模板方法模式:抽象类定义算法骨架,具体步骤延迟到子类。
  • 工厂方法模式:抽象类声明工厂方法,子类决定创建哪个具体对象。
  • 策略模式:有时用抽象类代替接口来提供公共基础功能。

posted @ 2026-08-15 12:32  春煦景明  阅读(7)  评论(0)    收藏  举报