通过继承我们可以定义一个新类,新类纳入一个已经声明的类并进行扩展
可以使用一个已经存在的类作为新类的基础。已存在的类称为基类(base class),新类称为派生类(derived class)
派生类成员的组成如下:
■ 本身声明中的成员
■ 基类的成员
要声明一个派生类,需要在类名后加入基类规格说明。基类规格说明由冒号和后面跟着用作基类的类名称组成。派生类被描述为直接继承自列出的基类
派生类
扩展它的基类,因为它包含了基类的成员
,加上在它本身声明中的新增功能。派生类不能删除它所继承的任何成员
继承的成员可以被访问,就像它们是派生类自己声明的一样
除了特殊的类object,所有的类都是派生类,即使它们没有基类规格说明。类object是唯一的非派生类,因为它是继承层次结构的基础
没有基类规格说明的类隐式地直接派生自类object。不加基类规格说明只是指定object为基类的简写。这两种形式是语义等价的
注意
一个类声明的基类规格说明中只能有一个单独的类。这称为
单继承
虽然类只能直接继承一个基类,但继承的层次没有限制。也就是说,作为基类的类可以派生自另外一个类,而这个类又派生自另外一个类,一直下去,直至最终到达object
基类
和派生类
是相对的术语。所有的类都是派生类,要么派生自object,要么派生自其他的类。所以,通常当我们称一个类为派生类时,我们的意思是它直接派生自某类而不是object
虽然派生类不能删除它继承的任何成员,但可以用与基类成员名称相同的成员来屏蔽(mask)基类成员。这是继承的主要功能之一,非常实用
例如,我们要继承包含某个特殊方法的基类。该方法虽然适合声明它的类,但却不一定适合派生类。在这种情况下,我们希望在派生类中声明新成员以屏蔽基类中的方法。在派生类中屏蔽基类成员的一些要点如下:
要屏蔽一个继承的数据成员,需要声明一个新的相同类型的成员,并使用相同的
名称
通过在派生类中声明新的带有相同签名的函数成员,可以隐藏
或屏蔽
继承的函数成员。请记住,签名由名称和参数列表组成,不包括返回类型
要让编译器知道你在故意屏蔽继承的成员,使用new
修饰符。否则,程序可以成功编译,但编译器会警告你隐藏了一个继承的成员
也可以屏蔽静态成员
如果派生类必须完全地访问被隐藏的继承成员,可以使用基类访问
(base access)表达式访问隐藏的继承成员。基类访问表达式由关键字base后面跟着一个点和成员的名称组成
语法:Console.WriteLine("{0}",base.成员名)
派生类的实例由基类的实例加上派生类新增的成员组成。派生类的引用指向整个类对象,包括基类部分
如果有一个派生类对象的引用,就可以获取该对象基类部分的引用(使用类型转换运算符把该引用转换为基类类型)。类型转换运算符放置在对象引用的前面,由圆括号括起的要被转换成的类名组成
当使用基类引用访问派生类对象时,得到的是基类的成员。虚方法可以使基类的引用访问“升至”派生类内
可以使用基类引用调用派生类(derivedclass)的方法,只需满足下面的条件:
⬛ 派生类的方法和基类的方法有相同的签名和返回类型
⬛ 基类的方法使用virtual
标注
⬛ 派生类的方法使用override
标注
其他关于
virtual
和override
修饰符的重要信息如下:
⬛ 覆写和被覆写的方法必须有相同的可访问性。换一种说法,被覆写的方法不能是private等,而覆写方法是public
⬛ 不能覆写static方法或非虚方法
⬛ 方法、属性和索引器(在前一章阐述),以及另一种成员类型事件(将在后面阐述),都可以被声明为virtual
和override
覆写方法可以在继承的任何层次出现
⬛ 当使用对象基类部分的引用调用一个覆写的方法时,方法的调用被沿派生层次上溯执行,一直到标记为override的方法的最高派生(most-derived)版本
⬛ 如果在更高的派生级别有该方法的其他声明,但没有被标记为override
,那么它们不会被调用
要创建对象的基类部分,需要隐式调用基类的某个构造函数作为创建实例过程的一部分
继承层次链中的每个类在执行它自己的构造函数体之前执行它的基类构造函数
默认情况下,在构造对象时,将调用基类的无参数构造函数。但构造函数可以重载,所以基类可能有一个以上的构造函数。如果希望派生类使用一个指定的基类构造函数而不是无参数构造函数,必须在构造函数初始化语句中指定它
有两种形式的构造函数初始化语句。
⬛ 第一种形式使用关键字base并指明使用哪一个基类构造函数。
⬛ 第二种形式使用关键字this并指明应该使用当前类的哪一个构造函数。
基类构造函数初始化语句放在冒号后面,冒号紧跟着类的构造函数声明的参数列表。构造函数初始化语句由关键字base
和要调用的基类构造函数的参数列表组成
类可以被系统中其他类看到并访问。这一节阐述类的可访问性。虽然我会在解说和示例中使用类,因为类是我们在书中一直阐述的内容,但可访问性规则也适用于以后将会阐述到的其他类型
可访问的
(accessible)有时也称为可见的
(visible),它们可以互换使用。类的可访问性有两个级别:public
和internal
C#允许从一个在不同的程序集内定义的基类来派生类
要从不同程序集中定义的基类派生类,必须具备以下条件:
⬛ 基类必须被声明为public,这样才能从它所在的程序集外部访问它
⬛ 必须在Visual Studio工程中的References节点中添加对包含该基类的程序集的引用。可以在Solution Explorer中找到该标题
要使引用其他程序集中的类和类型更容易,不使用它们的完全限定名称,可以在源文件的顶部放置一个using
指令,并带上将要访问的类或类型所在的命名空间
类的可访问性描述了类的可见性;成员的可访问性描述了类成员的可见性
声明在类中的每个成员对系统的不同部分可见,这依赖于类声明中指派给它的访问修饰符。你已经看到private
成员仅对同一类的其他成员可见,而public成员对程序集外部的类也可见。在这一节,我们将再次观察public
和private
访问级别,以及其他3个可访问性级别。
所有显式声明在类声明中的成员都是互相可见的,无论它们的访问性如何
继承的成员不在类的声明中显式声明,所以,如你所见,继承的成员对派生类的成员可以是可见的,也可以是不可见的
5个成员访问级别的名称 |
---|
public、private、protected、 internal、 protected internal |
敲黑板啦
必须对每个成员指定成员访问级别。如果不指定某个成员的访问级别,它的隐式访问级别为private
成员不能比它的类有更高的可访问性。也就是说,如果一个类的可访问性限于它所在的程序集,那么类的成员个体也不能从程序集的外部看到,无论它们的访问修饰符是什么,public
也不例外
public访问级别是限制性最少的。所有的类,包括程序集内部的类和外部的类都可以自由地访问成员
私有访问成员级别是限制最严格的
⬛private
类成员只能被它自己的类的成员访问。它不能被其他的类访问,包括继承它的类
⬛ 然而,private
成员能被嵌套在它的类中的类成员访问
protected
访问级别如同private
访问级别,除了一点,它允许派生自该类的类访问该成员
注意,即使程序集外部继承该类的类也能访问该成员
标记为internal
的成员对程序集内部的所有类可见,但对程序集外部的类不可见
标记为protected internal
的成员对所有继承该类的类以及所有程序集内部的类可见
注意,允许访问的集合是protected
修饰符允许的类的集合加上internal
修饰符允许的类的集合
注意,这是protected
和internal
的并集,不是交集
修饰符 | 说明 |
---|---|
public | 对任何类可访问 |
private | 只在类的内部可访问 |
protect | 对所有继承该类的类可访问 |
internal | 对该程序集内所有类可访问 |
protect internal | 对所有继承该类的类或程序集内声明类可访问 |
抽象成员是指设计为被覆写的函数成员。抽象成员有以下特征:
⬛ 必须是一个函数成员。也就是说,字段和常量不能为抽象成员
⬛ 必须用abstract修饰符标记
⬛ 不能有实现代码块。抽象成员的代码用分号表示
抽象成员
只可以在抽象类
中声明
有4种类型可以声明为抽象的:
方法;属性;事件;索引
关于抽象成员的其他重要事项如下:
⬛ 尽管抽象成员必须在派生类中用相应的成员覆写,但不能把virtual
修饰符附加到abstract
修饰符
⬛ 类似虚成员,派生类中抽象成员的实现必须指定override
修饰符
抽象类就是指设计为被继承的类。抽象类只能被用作其他类的基类
⬛ 不能创建抽象类的实例
⬛ 抽象类使用abstract
修饰符声明
⬛ 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带实现的成员的任意组合
⬛ 抽象类自己可以派生自另一个抽象类
⬛ 任何派生自抽象类的类必须使用override关键字实现该类所有的抽象成员,除非派生类自己也是抽象类
抽象类必须用作基类,它不能像独立的类那样被实例化。密封类与它相反
⬛ 密封类只能被用作独立的类,它不能被用作基类
⬛ 密封类使用sealed修饰符标注
静态类中所有成员都是静态的。静态类用于存放不受实例数据影响的数据和函数。静态类的一个常见的用途可能就是创建一个包含一组数学方法和值的数学库
关于静态类需要了解的重要事情如下:
⬛ 类本身必须标记为static
⬛ 类的所有成员必须是静态的
⬛ 类可以有一个静态构造函数,但不能有实例构造函数,不能创建该类的实例
⬛ 静态类是隐式密封的,也就是说,不能继承静态类
可以使用类名和成员名,像访问其他静态成员那样访问它的成员
目前为止每个方法都和声明它的类关联。扩展方法特性扩展了这个边界,允许编写的方法和声明它的类之外的类关联
扩展方法的重要要求如下:
⬛ 声明扩展方法的类必须声明为static
⬛ 扩展方法本身必须声明为static
⬛ 扩展方法必须包含关键字this
作为它的第一个参数类型,并在后面跟着它所扩展的类的名称
语法:访问修饰符 static 数据类型 扩展方法名 (this 类型 )
编写程序时会出现很多名称:类的名称、变量名称、方法名称、属性名称和许多其他名称。阅读程序时,使用命名约定是为要处理的对象种类提供线索的重要方法
风格名称 | 推荐使用 |
---|---|
Pascal (帕斯卡) | 类、方法、命名空间、属性和公共字段 |
驼峰式 | 局部变量的名称、方法声明的形参名称 |
_下划线+驼峰式 | 私有和受保护的字段 |