今天我們來談談C#中子類和父類中靜態成員以及建構函式的執行順序,這個地方向來是初學C#的人比較迷惑的地方,也是各大公司最喜歡拿來出面試題的地方。
下面我們分情況來分析。
1. 普通建構函式和靜態建構函式的執行順序。
對於單個的類,它的靜態建構函式將先於普通建構函式執行,因為普通建構函式需要在執行個體化該類的時候執行,而靜態建構函式訪問該類的同時就開始執行。
而對於有繼承關係的父類和子類來說,父類的普通建構函式先於子類的普通建構函式執行,簡而言之就是被依賴的先構造,依賴於人的後構造。
我們來看執行個體。
我們為父類和子類分別定義靜態建構函式和普通建構函式。
public class Parent
{
static Parent()
{
Console.WriteLine("Parent Static Constructor");
}
public Parent()
{
Console.WriteLine("Parent Constructor")
}
}
定義一個子類繼承於該父類。
public class Child : Parent
{
static Child()
{
Console.WriteLine("Child Static Constructor");
}
public Child()
{
Console.WriteLine("Child Constructor");
}
}
那麼當我們執行個體化Child類的時候會如何輸出呢?
static void Main(string[] args)
{
Child child = new Child();
Console.Read();
}
結果如下
這個結果很好解釋,我們執行個體化子類,所以靜態建構函式在訪問類的時候同時訪問,所以先執行子類的靜態建構函式,而Child類依賴於Parent類,所以接下來執行Parent類的普通建構函式。
兩個類的靜態建構函式執行完畢後分別執行父類的普通建構函式和子類的普通建構函式。
2. 靜態變數,靜態建構函式以及普通建構函式的情況。
從上面分析的第一種情況我們知道靜態建構函式是先於普通建構函式執行的, 而靜態變數是先於靜態建構函式執行的。
那如果該靜態變數定義的是該類的一個執行個體執行順序如何呢?
我們來看執行個體。
public class Child
{
static public Child instance = new Child();
static Child()
{
Console.WriteLine("Child Static Constructor");
}
public Child()
{
Console.WriteLine("Child Constructor");
}
}
執行個體化該類,顯示結果如下:
對於這個執行結果,首先執行的是靜態變數,而該靜態變數是對Child類的執行個體化,此時先調用它的普通建構函式,為什麼不會調用它的靜態建構函式呢?因為靜態建構函式的執行在靜態變數之後,
所以第一步,初始化靜態變數,列印 "Child Constructor" ;
第二步,執行靜態建構函式,列印 "Child Static Constructor" ;
第三步,執行普通建構函式, 列印 "Child Constructor".
3. 存在繼承關係的靜態變數,靜態建構函式,普通建構函式的執行順序.
下面我們把第二種情況複雜化一點,加入父類。
public class Parent
{
static Parent()
{
Console.WriteLine("Parent Static Constructor");
}
public Parent()
{
Console.WriteLine("Parent Constructor");
}
}
子類繼承於父類。
public class Child :Parent
{
static public Child instance = new Child();
static Child()
{
Console.WriteLine("Child Static Constructor");
}
public Child()
{
Console.WriteLine("Child Constructor");
}
}
如果我們執行個體化Child類,會產生什麼結果呢?在我給出答案之前,大家不妨先分析一下這個結果。
1. 先初始化Child類的靜態變數instance。
顯然最開始執行的是Child類中的靜態變數instance,instance要執行個體化一個Child類,那麼首先要訪問Child類的普通建構函式,而Child類又是依賴於Parent類的,所以在執行Child類的
建構函式之前,先執行的是它父類的靜態建構函式和普通建構函式(注意此時Child類的靜態建構函式不會執行)。它將列印出"Parent Static Constructor"和"Parent Constructor".
其次執行這個普通建構函式,(此時Child類的靜態建構函式仍然不會執行)。列印出"Child Constructor".
2. 執行Child類的靜態建構函式
這次將列印出"Child Static Constructor"。
3. 執行Child類的普通建構函式
執行Child類的普通建構函式,這裡會產生兩個結果。首先,會執行它父類的普通建構函式,注意此時父類的靜態建構函式不會執行了,它只會執行一次。其次才是執行Child類自己的普通建構函式。
因此此時應該列印出"Parent Constructor"和"Child Constructor".
實際啟動並執行結果也證明剛才分析的正確性。
通過上面三個例子的分析,相信大家應該知道了如何去分析這個執行順序,那就給大家留一個作業自己思考吧。
父類:
public class Parent
{
static public Parent instance = new Parent();
static Parent()
{
Console.WriteLine("Parent Static Constructor");
}
public Parent()
{
Console.WriteLine("Parent Constructor");
}
}
子類:
public class Child :Parent
{
static public Child instance = new Child();
static Child()
{
Console.WriteLine("Child Static Constructor");
}
public Child()
{
Console.WriteLine("Child Constructor");
}
}
}