首页 / C# 入门教程 / 密封类与密封方法

C# 入门教程

密封类与密封方法

本教程共 100 篇 · 第 46 篇 · 更新于 2026-07-31 · 约 10 分钟阅读

C#C# 入门教程面向对象密封类

46. 密封类与密封方法

本节目标:用 sealed 禁止类被继承或方法被重写,理解它适合的场景。

sealed 是什么

sealed 意为”密封”。修饰类时,这个类不能再被别人继承;修饰方法时,这个被重写的方法不能再被下一代改写。它是 virtual/override 的反面,用来”封死扩展点”。

可以把它想成工厂流水线上的封条:产品到了这一站就封箱,后面不许再拆开改装。继承让我们自由扩展,但有时自由反而带来风险。sealed 就是一道”到此为止”的边界,它告诉后来的开发者:这一层就是终点,别再往下改。

密封类

给类加 sealed,任何想继承它的尝试都会编译失败。

namespace CSharpDemo;

sealed class Logger
{
    public void Write(string msg) => Console.WriteLine(msg);
}

Logger log = new();
log.Write("记录一条日志");

// 下面这行会报错:无法从密封类继承
// class FileLogger : Logger { }

静态类(static class)天生就是密封的,你本就继承不了它。所以给静态类再加 sealed 是多余的,编译器虽不报错,但毫无意义。

密封方法

override 前加 sealed,可阻止再往下的派生类继续改写该方法(见第 41 章方法重写)。

namespace CSharpDemo;

class Animal
{
    public virtual string Sound() => "动物叫";
}

class Dog : Animal
{
    public sealed override string Sound() => "汪汪";
}

// 下面这行会报错:无法再重写 Dog.Sound
// class Bulldog : Dog { public override string Sound() => "呼噜"; }

Dog 之后任何类继承 Dog,都不能再改 Sound。注意:密封方法必须写在 override 上,不能单独修饰普通虚方法。语法要求”先重写、再封死”——如果父类方法没标 virtual,你根本没法 override,自然也谈不上 sealed override

为什么这样设计

sealed 的存在理由主要有三个,理解它们才不会”为了用而用”。

第一是安全。某些类的逻辑一旦被篡改会出大问题(如加密、权限校验、金额计算)。封死继承,别人就无法派生子类覆盖关键行为。

第二是性能。虚方法调用要在运行期查表分派(virtual dispatch)。JIT 编译器遇到 sealed 类或方法,可以做”去虚拟化(devirtualization)“,直接内联调用,少一次查表。对高频调用的小类型,这点开销值得省。

第三是设计意图明确。当你确认”这个类型不该被扩展”,主动 sealed,等于在代码里写下一句文档:别从这里派生。

Warning

别把 sealed 当”私有”用。它只管能不能继承,完全不管成员的可见性。想隐藏实现请用 private/protected。两者管的事完全不同,混淆会带来错误的安全感。

什么时候该用 sealed

遇到下面几类情况,主动密封是负责任的选择:

  1. 安全敏感类型:加密、鉴权、签名这类逻辑,一旦能被子类覆盖就可能被绕过。封死继承是最省心的防护。
  2. 行为已完整、无需扩展:工具类、常量容器、固定算法实现,本就不该被改。
  3. 性能热点上的小类型:高频调用的叶子类,密封后能帮 JIT 做去虚拟化,调用更快。
  4. 明确不希望被误用的基类:你清楚任何”扩展”都会破坏不变式(invariant),那就提前封死。

sealed 与抽象类、接口的对比

维度sealedabstract接口 interface
能否被继承不能必须被继承可被实现
能否实例化能(类本身)不能不能
设计意图封死扩展强制补完定义契约
能否共存与 abstract 互斥与 sealed 互斥可独立存在

一个类不可能既是密封又是抽象,逻辑上自相矛盾。一个阻止继承,一个强制继承,方向相反。接口则走另一条路:它不阻止继承,只规定”必须实现什么”。

Tip

默认不密封。只有当”被继承会带来真实风险或混乱”时,才主动密封。把 sealed 当作设计声明,而不是防御性过度约束。

什么时候不该用 sealed

反过来说,以下情况请慎用 sealed

  • 你只是”暂时没想到”别人要扩展,并不代表永远不需要。过度密封会逼别人复制你的代码。
  • 你在写一个可能被他人复用的库,过早封死会限制使用方的合理定制。
  • 类型本身很小、调用不频繁,性能收益可以忽略,密封只会增加认知负担。
Note

经验法则:sealed 是”收紧”,请慎用。先假设类型可被继承,直到你有明确理由封死它。库作者尤其要克制,因为封死的扩展点是再也打不开的。

新手常见坑

第一,把 sealed 当”私有”用。它只管能不能继承/重写,不管成员可见性,二者是两回事。

第二,过度密封导致别人无法在合理处扩展,反而要绕路复制代码。密封是”收紧”,请慎用。

第三,结构 struct 本就不能被继承,给 structsealed 是多余的。

第四,以为密封方法能让整个类不可继承。它只封死那一个方法,类照样能被继承,只是该方法不能再被覆盖。

第五,在密封方法上又试图 virtualsealedvirtual 不能同时修饰同一成员,二者语义直接冲突。

常见疑问解答

问:密封类还能被实例化吗? 能。sealed 只禁止”被继承”,不影响 new 创建对象。工具类、日志类经常既是 sealed 又能正常 new

问:sealed 能修饰字段或属性吗? 不能。它只能修饰类,以及在 override 上的方法/属性。想锁住字段请用 readonly,锁住属性赋值请用 init

问:接口里的默认方法能 sealed 吗? 可以。在接口中用 sealed override 能阻止实现类再改这个默认实现(见第 45 章)。这是 sealed 在接口维度的延伸。

问:我该默认把所有类都 sealed 吗? 不。默认开放继承,只在有充分理由时封死。盲目全密封会让代码僵化,别人想扩展只能复制,反而更糟。

问:密封方法和私有方法有何不同? 私有方法是”外面根本看不到”,密封方法是”看得到、能调用,但不能再重写”。一个是可见性,一个是可重写性,处在不同层面。

小结

密封是面向对象里”收口”的工具:该开放时开放,该封死时封死。sealed 用得克制而精准,代码既安全又高效。

讲完继承与多态的开放,我们回到底层:所有类型共同的祖宗 object