什么时候必须用 sealed
?不是“可选”,而是设计刚需
当你写的类承载了不可妥协的核心逻辑(比如资金扣减、密钥派生、权限校验),就必须用
sealed。这不是防御性编码,是架构边界——它让编译器在编译期就堵死所有继承路径,避免子类通过
override悄悄绕过安全检查。 支付类
Payment若未密封,别人继承后重写
Pay()方法,可能把
amount * 0.99当成扣款逻辑 日志类
SecureLogger若允许继承,子类可能覆盖
Log()并跳过敏感字段脱敏 .NET 内置的
string和
DateTime都是
sealed,因为它们的语义和行为必须绝对稳定
sealed
类为什么能提升性能?JIT 真的会优化它
编译器知道
sealed类永远不会被继承,所以对它的虚方法调用(哪怕声明为
virtual)可以跳过虚表查找,直接生成内联或直接调用指令。实测在高频循环中,
sealed类的方法调用比普通类快约 8%–12%(基于 .NET 6+ JIT 数据)。 这个优化只对实例方法生效;静态方法不受影响 如果类里有大量
virtual成员但又不打算被继承,不加
sealed就等于主动放弃 JIT 优化机会 工具类如
MathHelper加
sealed不仅是语义清晰,更是白送的性能红利
常见错误:误以为 sealed
可以和 abstract
共存
这是编译器会立刻报错的组合:
public abstract sealed class X—— 直接拒绝编译。因为
abstract要求必须被继承实现,而
sealed明确禁止继承,二者逻辑互斥。 错误现象:
'X' cannot be both abstract and sealed真实需求场景:你想限制继承但又需要提供抽象契约?→ 改用接口(
interface)+ 密封实现类 结构体(
struct)天然隐式
sealed,无需、也不能显式加该关键字
密封方法(sealed override
)比密封类更细粒度的控制
当你的类本身需要被继承(比如框架基类),但其中某个关键方法绝不能被下游再改写,就用
sealed override。它像给继承链中的某一个齿轮上了锁。 必须同时满足:父类方法是
virtual→ 子类用
override重写 → 再加
sealed阻止进一步重写 示例:
public sealed override void Validate() { ... },那么 class Child : Parent就无法再
override Validate注意:单独写
sealed void Validate()(没
override)是语法错误
真正容易被忽略的点是:密封不是“防黑手段”,而是“防误用手段”。很多团队加
sealed是为了防止新人在不了解上下文时盲目继承,结果破坏了不可变性或线程安全契约。一旦决定密封,就要同步清理掉所有
virtual成员——否则留着虚方法却禁止继承,反而造成语义混乱。
