sealed 是编译器层面的继承/重写拦截器,禁止派生和覆盖而非锁住代码;修饰类时继承即报错,修饰方法需与 override 成对出现以封禁后续重写,不提供运行时保护。

sealed 不是“锁住代码”,而是编译器层面的继承/重写拦截器——它不阻止调用,只阻止派生和覆盖。
sealed 修饰类:一写继承就报错,不是运行时才拦
当你声明 sealed class Logger,任何尝试写 class MyLogger : Logger 的代码都会在编译阶段失败,错误信息明确是:'MyLogger' cannot inherit from sealed class 'Logger'。
- 这不是设计建议,是硬性约束:编译器直接拒绝生成 IL,连 JIT 都没机会介入
- 适用场景包括:工具类(
JsonSerializer封装)、值对象(UserIdentity)、密码哈希器等——你不想让下游通过继承绕过校验逻辑 - 常见误踩:和
abstract同时出现 → 编译报错,语义冲突(一个强制被继承,一个禁止被继承) - 性能影响真实存在:JIT 对
sealed类的虚方法调用可做去虚拟化(devirtualization),调用开销更低;反射查成员也略快(不用遍历继承链)
sealed override 方法:只封“这一层”的重写权
sealed 不能单独用于方法,必须和 override 成对出现。它的作用不是禁止调用,而是禁止后续派生类再覆盖这个方法。
- 典型写法:
public sealed override void ValidateInput()—— 允许子类继承当前类,但关键流程节点不许改 - 错误示范:
public sealed void DoWork()→ 编译失败,缺少override - 如果父类方法没声明
virtual或abstract,子类用new隐藏它,那根本不能、也不需要加sealed - 注意:
struct默认隐式sealed,所以sealed struct S { }会直接报错
为什么 string 和 DateTime 是 sealed 的?不是为了“防扩展”,而是保契约
.NET 基础类型大量使用 sealed,根本原因不是“不让你动”,而是防止破坏底层依赖的语义契约。
-
string密封 → 避免有人继承后重写Length或==行为,否则LINQ.Where、System.Text.Json等组件可能行为异常 -
DateTime密封 → 确保其不可变性(immutability)不被子类通过重写AddDays()等方法破坏 - 它和
private无关:sealed类的public成员仍可被任意代码调用,只是不能被继承 - 它也不影响接口实现:
sealed class A : ICloneable完全合法,密封只管“能不能被继承”,不管“能不能实现接口”
最容易被忽略的一点是:sealed 不提供运行时保护,它只在编译期生效;一旦 DLL 已发布,别人没法继承你的 sealed 类,但依然能用反射调用其所有 public 成员——别把它当成安全沙箱用。










