先搞清楚一个核心事实:abstract 类不能直接 new 实例。这是最常踩的坑——编译器会立刻报错 Cannot create an instance of the abstract class or interface。它不是“高级版普通类”,而是设计用来被继承的契约骨架。
为什么 new 不了 abstract 类?
因为 abstract 类里可能有没实现的成员,比如 abstract 方法或属性。C# 要求所有非抽象子类必须补全这些空缺,否则连编译都过不去。
- 哪怕类里只有一个
abstract方法,整个类自动变成abstract,且不可实例化 abstract类可以有构造函数,但只能被子类的: base(...)调用,不能手动 new- 误写
new MyBaseClass()会触发编译错误,不是运行时异常
abstract 方法必须用 override 实现
子类实现抽象成员时,必须显式加 override,不能只写方法签名。漏掉 override 会导致编译失败,提示 No suitable method found to override 或 Member must ha ve 'override' keyword。
- 抽象方法声明末尾是分号,没有大括号:
public abstract int Area(); - 子类实现必须带
public override int Area() { ... },访问修饰符不能比父类更严格(如父类public,子类不能写protected) - 不能在抽象方法上加
static、virtual或sealed—— 编译器直接拒绝
接口和 abstract 类混用时的常见陷阱
当一个 abstract 类实现接口(如 IComparable),它可以不提供接口方法的具体实现,而是把责任推给子类——只要把接口方法也声明为 abstract 即可。
- 例如:
abstract class Shape : IComparable { public abstract int CompareTo(object obj); } - 这样子类只需实现一次
CompareTo,就同时满足接口契约和抽象类约束 - 但如果子类忘了实现,错误信息可能指向接口而非抽象类,容易误判源头
- 注意:抽象类必须“声明”所有接口成员,哪怕只是用
abstract转发过去;否则编译报错'X' does not implement interface member 'Y'
别把 abstract 和 sealed 放在一起
这两个关键字语义冲突,C# 禁止同时使用。试图写 sealed abstract class C 会得到编译错误 error CS0418: 'C': an abstract class cannot be sealed or static。
abstract的核心诉求是“必须被继承”,sealed的核心诉求是“禁止被继承”- 如果想阻止进一步派生,应该让子类用
sealed,而不是污染抽象基类 - 抽象类里的方法默认是隐式
virtual,所以子类能override;不需要、也不允许显式加virtual
真正难的不是语法,而是判断哪些逻辑该放进抽象层、哪些留给子类决定。比如 Draw() 可以是抽象的,但通用日志记录或参数校验往往适合放在抽象类的非抽象方法里——这部分容易被忽略,结果导致重复代码或行为不一致。