先看个例子:
internal class Parent { int x = 1; private readonly int y; public Parent() { // 位置1 this.y = -2; PrintFields(); } public Parent(int y) : this() { // 位置2 this.y = y; } public virtual void PrintFields() { Console.WriteLine("Parent:x={0},y={1}", x, y); } } internal class SonTom : Parent { int x = 3; private int y; public SonTom() { // 位置3 y = -3; } public override void PrintFields() { Console.WriteLine("SonTom: x={0},y={1}", x, y); } } internal class SonJerry : Parent { int x = 2; private int y; public SonJerry() { // 位置4 y = -3; PrintFields(); } public new void PrintFields() { Console.WriteLine("SonJerry: x={0},y={1}", x, y); } }
调用并运行:
var parent = new Parent(22); // Parent:x=1,y=-2 原因解释: 初始化时先执行this()方法,此时的y=-2; 然后才执行Parent(int y),此时的y=22,但是已经不输出了 var d = new SonTom(); // SonTom: x=3,y=0 原因解释: SonTom子类私有字段先初始化x=3,y=0; Parent私有字段初始化 x=1,y=0; Parent构造函数执行 x=1,y=-2; 调用PrintFields(),因为方法被重写(override)了,所以调用的是子类的PrintFields(),此时x=3,y=0; 最后调用子类的构造函数 x=3,y=-3 Console.WriteLine(); Parent bc = new SonJerry(); // x=1,y=-2 // x=2,y=-3 原因解释: SonJerry子类私有字段先初始化x=2,y=0; Parent私有字段初始化 x=1,y=0; Parent构造函数执行 x=1,y=-2; 调用PrintFields(), 因为方法被子类覆盖(new)了,所以父类构造函数中调用的是父类自己的PrintFields()输出x=1,y=-2; 最后调用子类的构造函数 x=2,y=-3, 因为用new覆盖了, 且Parent.x是私有的, 调用子类的PrintFields()时输出子类自己的 x=2,y=-3 子类的私有变量不会被父类的私有变量覆盖
先解释下 readonly( 简单来说,在此例中只可以在构造函数中多次修改值)
在字段声明中,readonly 指示只能在声明期间或在同一个类的构造函数中向字段赋值。 可以在字段声明和构造函数中多次分配和重新分配只读字段。
构造函数退出后,不能分配 readonly 字段。 此规则对于值类型和引用类型具有不同的含义:
1)由于值类型直接包含数据,因此属于 readonly 值类型的字段不可变。
2)由于引用类型包含对其数据的引用,因此属于 readonly 引用类型的字段必须始终引用同一对象。 该对象是可变的。 readonly 修饰符可防止字段替换为引用类型的其他实例。 但是,修饰符不会阻止通过只读字段修改字段的实例数据。
由此推出顺序为:
1)子类静态成员变量初始化
2)子类实例变量初始化
3)父类静态静态成员变量初始化
4)父类实例变量初始化
5)父类构造方法调用
6)子类构造方法调用。