继承与构造函数的调用
1 class A
2 {
3 public A()
4 {
5 PrintFields();
6 }
7 public virtual void PrintFields()
8 {
9
10 }
11 }
12 class B : A
13 {
14 int x = 1;
15 int y;
16 public B()
17 {
18 y = -1;
19 }
20 public override void PrintFields()
21 {
22 Console.WriteLine("x={0},y={1}", x, y);
23 }
24 }
new B();后结果是:x=1,y=0
说明:y=0是因为执行到B的构造函数时,会先执行父类就是A的构造函数,而A的构造函数又调用了PrintFields()方法,这个方法在A中为虚方法,在B中被重写,所以执行的是重写后的PrintFields()方法,然后进行输出(Console.WriteLine),这时还没有执行到y=-1这句代码,然而int未赋值时,默认为0。
以下是网上比较靠谱的说法:
输出 1,0
B b=new B();
在执行时CLR会加载B类并为其分配空间,首先加载成员变量,再加载方法列表.
那么x,y会被加载并分配空间和初始化数据,这时候x=1,y=0
然后加载方法表,先父类再子类的顺序,
A:A()
A:PrintFields()--注意这是个虚方法
再子类自己的
B:B()
B:PrintFields() --override
注意,在这里加载子类的方法的时候会把A:PrintFields()覆盖掉,所以在B的方法表中将不再出现父类的方法.
在代码中执行产生对象的时候先调用父类的构造函数A()再B();
这时候A()调用方法PrintFields(),而这个方法的调用在方法列表中已经被覆盖
在调用方法列表中的方法的时候我们发现父类的方法在方法类表中不存在就
不再调用父类的方法,调子类的方法.
因为还没调到B类自己的构造函数,所以Y不是-1.
即输出 1,0
---------------------------------------------------------
当一个EXE或者DLL被调用或者执行的时候,CLR首先加载该EXE或者DLL中的
数据.其实就是程序集.大家都知道程序集由:程序集清单,类型元数据,IL和资源文件组成
其他的我们不说,在程序运行前,类型元数据必须先加载.就是我们在EXE中定义的所有的类型,
类型的成员,类型成员的信息等等.
那么上面我们的问题:当编写的程序运行时,还没进入Main之前.
CLR就会加载A类和B类型
在A类型中有几个列表:成员字段列表,方法列表(我们暂且就说这两个);
A的成员字段列表没任何数据,方法列表带两个方法:A()和virtual void PrintFields()
B的成员字段列表包含两个字段:int x和int y,因为B:A,但是A没有成员字段,所以只带自己的成员
方法列表:因为继承的关系,先加载A()和virtual void PrintFields(),然后再加载B()
和override void PrintFields(),当加载的时候CLR发现override就会把继承的父类的方法,覆盖,
则,最终B的方法列表为:A(),B(),override void PrintFields()只有三个.
当我们调用B b=new B()的时候,CLR根据B的类型历遍类型元数据的方法表和字段表,然后在内存中
开辟一个空间,并对数据进行初始花,也就出现了x=1,y=0,然后根据方法表调用A()执行IL的中间代码,
执行过程中而又调用了PrintFields(),就会在方法表中找到PrintFields(),然后执行IL中的代码,而
这个PrintFields()对应的IL代码是被重写的方法,所以就执行了输出 从内存中取到x,y的数据输出
产生第一次输出 1,0.
然后调用自己的构造函数B(),将空间分配完成.并将y赋为-1.然后将该空间的地址引用到变量b上,完成对象的实例化工作.
-------------------------------------------------------------
经无数测试,以及查找资料,这道题只有一个输出:x=1,y=0 (说有两个输出的人请看清楚题目,并自己运行下)
原因:
在 new B()时候,调用B的构造函数,因为B:A,所以会构造A,即调用A的构造函数,而A的构造函数中调用一个virtual的方法PrintFields(),因为这个方法是虚拟的,并且在B中override,所以,就会直接运行B中的被override掉的方法(而不是返回去调用B的构造函数),另一方面,因为x,y在B的构造函数外已经定义,且x=1。而y没有被赋值,所以默认值为0.所以输出x=1,y=0
这题的两个要点:
1.如果构造子类,即调用子类的构造函数的话,基类的构造函数中对虚拟函数的调用不会被执行,因为它是在子类中override的,具体原因有待研究
2.int变量没有赋值时,默认值为0