# 密封函数 概念: 密封函数指的是使用 `sealed` 修饰的==重写成员== 在 C# 里常见写法是 `sealed override` 准确一点说: C# 里通常叫“密封方法”或“密封重写成员” 有些教程会说“密封函数”,初学阶段可以理解成:==不允许下一级子类继续重写的方法== 通常用法: 1. 父类允许子类重写某个方法 2. 某个子类重写后,认为这个实现已经定死 3. 允许这个子类继续被继承 4. 但不允许更下一级子类再改这个方法 ---- # 语法 ```csharp class 子类名 : 父类名 { public sealed override void 方法名() { } } ``` 注意: `sealed` 修饰方法时,必须和 `override` 一起使用 ---- # 基础案例 ```csharp class Animal { public virtual void Speak() { Console.WriteLine("动物叫"); } } class Dog : Animal { public sealed override void Speak() { Console.WriteLine("狗叫"); } } class Husky : Dog { // 这里不能再 override Speak() } ``` 理解: 1. `Animal.Speak()` 是虚方法,允许被重写 2. `Dog.Speak()` 重写了父类方法 3. `Dog.Speak()` 又被 `sealed` 密封 4. `Husky` 可以继承 `Dog` 5. 但 `Husky` 不能继续重写 `Speak()` ---- # sealed class 和 sealed override 的区别 密封类: ```csharp sealed class Dog : Animal { } ``` 含义: 整个 `Dog` 类不能再被继承 密封函数: ```csharp class Dog : Animal { public sealed override void Speak() { Console.WriteLine("狗叫"); } } ``` 含义: `Dog` 类仍然可以被继承 但是 `Speak()` 不能再被下一级子类重写 对比: 1. `sealed class`:封住整个类 2. `sealed override`:只封住某个已经重写的成员 这个点可以和[[密封类]]一起记。 ---- # 为什么密封函数必须是 override 错误写法: ```csharp class Dog { public sealed void Speak() { } } ``` 原因: 普通方法本来就不能被 `override` 没有必要再用 `sealed` 封住 正确理解: `sealed` 修饰方法时,是在说: “这个方法原本处在可重写链条上,但从我这里开始不准继续重写” 所以它必须修饰已经重写的成员,也就是 `sealed override`。 ---- # 多级继承案例 ```csharp class A { public virtual void Test() { Console.WriteLine("A"); } } class B : A { public override void Test() { Console.WriteLine("B"); } } class C : B { public sealed override void Test() { Console.WriteLine("C"); } } class D : C { // public override void Test() // { // Console.WriteLine("D"); // } } ``` 调用: ```csharp A a = new C(); a.Test(); // C ``` 理解: 1. `A.Test()` 开启虚方法 2. `B.Test()` 继续重写 3. `C.Test()` 重写并密封 4. `D` 仍然可以继承 `C` 5. 但 `D` 不能再重写 `Test()` ---- # 密封属性 `sealed override` 不只可以用于方法,也可以用于属性。 ```csharp class Character { public virtual int MaxHp { get { return 100; } } } class Boss : Character { public sealed override int MaxHp { get { return 1000; } } } ``` 后续子类不能再重写 `MaxHp`。 ---- # 和多态的关系 密封函数不会取消已有多态效果。 ```csharp Animal animal = new Dog(); animal.Speak(); // 狗叫 ``` `Dog.Speak()` 仍然会通过多态被调用。 `sealed override` 只是限制: 后续子类不能继续改这个方法 也就是说: 1. 对上层父类来说,它仍然是重写后的多态方法 2. 对下层子类来说,它已经不能再被重写 ---- # 常见错误 错误1:普通方法前写 `sealed` ```csharp class A { public sealed void Test() { } } ``` `sealed` 不能这样修饰普通方法。 ---- 错误2:只写 `sealed` 不写 `override` ```csharp class B : A { public sealed void Test() { } } ``` 密封重写成员必须写成 `sealed override`。 ---- 错误3:父类方法不是虚方法 ```csharp class A { public void Test() { } } class B : A { public sealed override void Test() { } } ``` 父类方法不是 `virtual` / `abstract` / `override`,子类不能 `override`,也就不能 `sealed override`。 ---- # 什么时候用密封函数 适合用的情况: 1. 某个重写逻辑非常关键,不希望后续子类改掉 2. 父类设计允许多态,但某一层子类希望终止这个方法的重写链 3. 框架或基础类库中需要保证关键行为稳定 初学阶段: 先知道这个语法和作用即可 自己写练习代码时用得不多 ---- # 注意 1. 密封函数常见写法是 `sealed override` 2. 它只能用于已经重写的成员 3. 它阻止下一级子类继续重写这个成员 4. 它不会阻止当前类被继承 5. 如果想阻止整个类被继承,用 `sealed class` 6. 方法、属性、索引器、事件等重写成员都可以理解这个规则 ---- # 一句话记忆 ==sealed override 表示这个重写到我这里为止,后面的子类不能再改。== ---- # 面试/复习时怎么说 可以这样答: “密封函数通常指 `sealed override` 修饰的重写方法。 它表示当前类重写了父类的虚方法,但不允许更下一级子类继续重写。 它和密封类不同,密封类是不允许整个类被继承,而密封函数只是禁止某个重写成员继续被重写。” ---- 引用: 1. Microsoft Learn: [sealed (C# Reference)](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/sealed) 2. Microsoft Learn: [override (C# Reference)](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/override) #密封函数 #密封方法 #核心