【转载】c#类的成员初始化顺序
作者:彭白洋 创建于:2009-01-15 出处:http://www.cnblogs.com/siceblue/archive/2009/01/15/1376430.html 收录于:2013-02-28
看如下代码:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 DriveB d = new DriveB(); 6 Console.Read(); 7 } 8 } 9 class BaseA 10 { 11 //4.基类静态成员初始化; 12 static DisplayClass a = new DisplayClass("基类静态成员初始化"); 13 //6.基类实例成员初始化; 14 DisplayClass BaseA_c = new DisplayClass("基类实例变量BaseA_c初始化"); 15 //5.基类静态构造函数调用 16 static BaseA() 17 { 18 Console.WriteLine("基类静态构造方法被调用"); 19 } 20 //7.基类构造方法调用; 21 public BaseA() 22 { 23 Console.WriteLine("基类构造方法被调用"); 24 } 25 } 26 class DriveB : BaseA 27 { 28 //1.继承类静态成员初始化; 29 static DisplayClass DriveB_b = new DisplayClass("继承类静态成员DriveB_b初始化"); 30 //3.继承类实例成员初始化; 31 DisplayClass DriveB_c = new DisplayClass("继承类实例变量DriveB_c初始化"); 32 //2.继承类静态构造函数调用 33 static DriveB() 34 { 35 Console.WriteLine("继承类静态构造方法被调用"); 36 } 37 //8.继承类构造方法调用; 38 public DriveB() 39 { 40 Console.WriteLine("继承类构造方法被调用"); 41 } 42 } 43 class DisplayClass 44 { 45 public DisplayClass(string diplayString) 46 { 47 Console.WriteLine(diplayString); 48 Console.WriteLine(); 49 } 50 }
得出初始化顺序结论:
1)继承类静态成员变量初始化
2)继承类实例变量初始化
3)基类静态静态成员变量初始化
4)基类实例变量初始化
5)基类构造方法调用
6)继承类构造方法调用。
加了静态构造函数后结论:
1)继承类静态成员变量初始化
1.1)继承类静态构造函数调用
2)继承类实例变量初始化
3)基类静态静态成员变量初始化
3.1)基类静态构造函数调用
4)基类实例变量初始化
5)基类构造方法调用
6)继承类构造方法调用。
总结:
1 静态成员优先于实例成员
2 实例成员优先于构造函数
3 在调用子类实例构造函数之前,先调用基类实例构造函数
4 继承类静态构造函数不会调用基类构造函数,因为静态实例时放在类型对象中的。
和JAVA的有点不一样啊,像JAVA是严格的从基类到派生类
了解了静态成员初始化,就引出了另外一个问题,如果两个类相互间引用,比如A类的静态初始化里引用到了B类,B类的静态初始化里又引用到了A类,请看下面这段代码:
1 class A 2 { 3 public static int X; 4 static A() 5 { 6 X=B.Y+1; 7 } 8 } 9 class B 10 { 11 public static int Y=A.X+1; 12 static B(){} 13 static void Main() 14 { 15 Console.WriteLine("X={0},Y={1}",A.X,B.Y); 16 } 17 }
结果:x=1,y=2
解析:
一般来说静态声明赋值语句先于静态构造函数执行,没有赋值的类成员(局部变量不会有默认值)声明(这里不是初始化)会被初始化成该类型的默认值,所以刚开始x,y的默认值都是0.。
因为Main函数在class B中,所以程序先执行的是上面的第二条语句 (public static int Y=A.X+1;),声明一个Y,再给Y赋值
在赋值的时候又调用到了A类中的X静态,当第一次访问A.X的时候,会先调用A类的静态构造函数,这里执行赋值X=B.Y+1,而重新去访问B类的成员,因为前面说的静态初始化只有第一次被访问的时候会执行,所以再次访问B类的时候不会重复进行静态初始化的。这时会因为前一次初始化还未完成,特别是B.Y还没有赋值完成,所以根据上面说的,B.Y现在处理只是声明完成的状态,所以现在B.Y的值就是0,相应的得到的X的值就是1了,在A类的静态构造函数执行完成的时候,程序会再回到B中Y的赋值语句上来,这时候得到的A.X的值就是1,而Y赋值完成后,此时值就变成了2了。
为了说明上述红色字体语句,看一下代码和结果:
1 class Program 2 { 3 static void Main() 4 { 5 Console.WriteLine("X={0},Y={1}", A.X, B.Y); 6 Console.Read(); 7 } 8 } 9 class A 10 { 11 public static int X; 12 static A() 13 { 14 X = B.Y + 1; 15 } 16 } 17 class B 18 { 19 public static int Y = A.X + 1; 20 static B() { } 21 22 }
结果:x=2,y=1
对于引用类型成员的初始化说了这么多还是总结一下吧.C#中初始化变量(包括实例成员变量和静态成员变量)可以采用成员声明的地方赋值的方式,也可以采用构造函数的方式.我个人在使用实例对象的时候比较推荐采用构造函数的方式,因为构造函数赋值的方式执行的顺序是从父类到子类,这种顺序避免了子类成员变量的初始化过程引用了未赋值的父类成员变量.而且在构造函数中初始化变量可以采用更多的语句块,更多的判断逻辑来初始化,甚至可以加上结构化异常处理try{}catch{}来处理异常信息,远比单单一个赋值语句来得灵活.不过对于简单的内置基本类型(如int,Enum,string等)就无所谓在哪里进行初始化了.