一:继承的分类
从c#中的继承可以分为两种(其他面向对象的语言貌似也是这样)
实现继承(不能实现多重继承,子类只能从一个父类派生)
接口继承(可以从多个接口派生)
结构是值类型的,不支持实现继承,支持接口继承,可以派生于多个接口
二:修饰符
public |
任何代码均可访问 |
protected |
仅派生类型可访问,实例对象不可访问 |
internal |
程序集内部可访问 |
private |
只能在类内部访问 |
protected internal |
程序集内部派生类中 |
new |
用相同的签名覆盖基类中的成员 |
static |
成员不在类的具体实例上执行 |
virtual |
成员可以由派生类重写 |
abstract |
只定义成员的签名,没有实现代码 |
override |
该成员重写了基类中的相同签名的virtual成员,并允许被再次重写 |
sealed |
该成员重写了基类中的相同签名的virtual成员,并不允许被再次重写 |
三:
子类拥有父类的所有子类可见成员
这也是设计子类的目的之一,为了扩展父类的功能
四:重写
方法只要声明为virtual,就可以被子类中签名相同的方法重写(override)
当然子类也可以不重写这个方法
成员字段和静态函数都不能被声明为virtual
一个方法被修饰成override,这个方法仍旧可以被它的子类重写
五:覆盖
用new关键字可以隐藏基类的方法,字段
这个感觉没什么好说的
综合例子一(看输出结果之前,希望你能仔细想以想)
输出结果为:
很多面试题中都有类似的题
我做个总结
一般情况下变量的类型是什么,该变量就拥有什么类型的成员
即使像下面这种情况也不例外
baseClass b2 = new sonClass();
b2的成员是baseClass中的成员
b2与sonClass中的成员无关
只有一种情况除外
当父类中的virtual方法已经被子类中的方法override过之后(new重写过之后是不行的)
类似这种情况baseClass b2 = new sonClass();
b2拥有的是重写过的方法成员
具体的原理以后有机会分析一下IL代码
那么我们总结一下这个现象
每个类型都有自己的类型成员表,虚方法成员是动态绑定的,运行时动态覆盖
综合例子二
输出为
就不多解释了
六:通过base关键字获取基类的成员
看个比较特殊的例子
输出为:
由此可见重写方法是可以通过base关键字调用被重写的方法的
基类的成员表在重写方法中是可见的
七:抽象类和抽象方法(abstract)
抽象类不能实例化
抽象方法没有执行代码
如果类包含抽象方法,那么该类也必须声明为abstract
当然一个声明为abstract的类可以包含实例方法
抽象方法与虚方法类似,也是运行时动态绑定的
八:密封类和密封方法(sealed)
密封类不能被继承
sealed关键字只能修饰重写(override)方法
密封方法不能被重写
但是可以通过new关键字覆盖它
除非特殊情况最好少用这个关键字
九:继承关系中的构造函数
初始化一个类的实例的具体步骤是
1:初始化该类的字段
2:初始化基类的字段
3:初始化基类的构造函数
4:初始化该类的构造函数
可以通过base关键字制定初始化基类中的哪个构造函数
再看个综合例子,你觉得应该输出什么
输出为:
就不多解释了
十:接口继承
接口继承和实现继承其实差不多
做几点说明:
1.一个类可以实现多个接口
2.不允许提供接口中任何成员的实现方式
3.接口只能包含方法,属性,所引器和事件,不允许包含运算符重载
4.不能实例化接口,因此接口不能有构造函数
5.不能声明成员修饰符,接口成员总是公共的,也不能声明成员为虚拟的或者静态的,这些是具体实现的时候做的事情
做此文得到了郑州的Xuefly的支持,在此表示感谢