前言
本章节继续讲实现继承.
实现继承
密封类和密封方法
密封类和方法的概念很简单, 就是为了不允许类和方法被继承和扩展. 不允许扩展一般的原因有:
如果类或者方法被扩展, 可能会导致类库执行错误
因为版权原因, 不允许第三方随意扩展该类
.Net 库有很多密封类, 使用者不能随意扩展. 我猜测这么做可能是因为设计者想保持框架的纯净性和单一性, 即不希望使用者随意扩展而导致出现众多 "分支框架", 最终的导致框架的碎片化(参考 Andriod 的碎片化).
最典型的, string 类型就是密封类. 我们可以猜测扩展方法的出现 (.Net3.5 版本) 就是因为对. Net 框架密封类有扩展的需求, 但是密封类无法继承, 所以出一个扩展方法作为 "补偿办法", 一定程度上解决了对基础框架的扩展问题.
密封类案例
- /// <summary>
- /// 密封类案例: 不能被继承, 其方法不能被 override. 这个类同时还是基类
- /// </summary>
- public sealed class SaUser
- {
- public void Test()
- {
- // 密封类方法不能有 virtual 关键字, 因为 virtual 代表可 override, 这和密封类的概念矛盾了
- }
- /// <summary>
- /// 此写法为非法, 不同通过编译: 基类中不能定义密封方法
- /// </summary>
- public sealed override void Test2()
- {
- //Test2()不能定义 sealed, 也不能定义 sealed override, 都会导致编译异常.
- }
- }
密封方法案例
- /// <summary>
- /// 普通基类
- /// </summary>
- public class SaUser2
- {
- public virtual void Test()
- {
- // 可扩展的方法
- }
- }
- /// <summary>
- /// 派生类的密封方法
- /// </summary>
- public class SaUserChilid : SaUser2
- {
- /// <summary>
- /// 密封方法必须是重写的方法
- /// </summary>
- public sealed override void Test()
- {
- // 密封方法必须同时有 sealed 和 override 关键字
- base.Test();
- }
- }
有趣的现象
如上代码, 有个有趣的现象是, 定义密封类只需要使用 sealed 关键字, 而定义密封方法时, sealed 必须和 override 配对使用. 同时, 在基类中也不能对方法实行密封. 为什么会这样呢? 我猜想, 设计者的观点应该是:"所谓密封, 应该是对一个原先处于" 开放 "状态下的类进行密封". 但对密封的这个限制, 确实使得语言特性不够灵活. 实际上, 对基类的方法进行密封, 虽然极少发生, 但理论上还是有需求的.
派生类的构造函数
前面我们讲过了单个类的构造函数. 我们知道构造函数是必须的, 当我们没有类的构造函数时, 系统会默认提供一个构造函数, 因为类中字段的数据初始化是依赖于构造函数的.
当我们还要构造较为复杂的派生类时, 其构造函数如何运行, 就成为一个非常值得研究的问题. 研究清楚了它, 你才能用 "合适" 的办法完成派生类的初始化. 派生类的构造函数的麻烦, 来自于类的 "多构造函数", 也就是构造函数的重载.
首先, 我们要知道构造函数的原则:
每个类 (基类, 派生类) 的构造函数都是必须的.
类的构造是自底向上来构造的, 先构造基类, 再逐级向上构建派生类.(为什么必须这样逐级构造, 可以看原文章了解)
类的构造方法是可重载的(多构造函数).
在以上原则下, 当一个派生类要开始构造时, 我们发现, 关键要注意什么? 是构造链不能断! 在这点上编译器会智能判断, 如果它发现构造链断了, 会发生编译错误. 我们可以看个例子, 这个例子我以前的文章就贴过.
- /// <summary>
- /// 基类
- /// </summary>
- class Line
- {
- private int thick;
- /// <summary>
- /// 基类构造方法: 代号 A
- /// </summary>
- Line()
- {
- thick = 1;
- }
- }
- /// <summary>
- /// 派生类
- /// </summary>
- public class Rect
- {
- private int width;
- private int height;
- /// <summary>
- /// 构造方法: 代号 B
- /// </summary>
- /// <param name="length"></param>
- Rect(int length)
- :this(length, length) // 初始化器
- {
- // 构造一个正方形
- }
- /// <summary>
- /// 构造方法: 代号 C
- /// </summary>
- /// <param name="width"></param>
- /// <param name="height"></param>
- Rect(int width, int height)
- :base() // 初始化器
- {
- // 构造一个长方形
- this.width = width;
- this.height = height;
- }
- }
我给代码中的构造方法都加了代号. 以上的 demo, 我们可以分析得出, 如果使用 B 方法来构造对象, 它的构造链是: A->C->B. 如果用 C 方法来构造对象, 构造链是: A->C. 构造链是清晰的, 就没有问题.
修饰符
重申一下何为修饰符: 应用于类型或者成员的关键字.
可见性修饰符
不能把类型定义为 protected,private,protected interval. 嵌套类除外, 因为类成员可以使用访问限制性修饰符, 而嵌套类是和类的成员同等级的.
其他修饰符
讲完继承, 下篇开始讲解接口.
来源: https://www.cnblogs.com/holyknight17/p/10342971.html