初始化裡又引用到了A類,這個時候又會出現什麼樣的結果呢,還是用例子還說明吧,請看下面這段代碼:
1using System;
2 class A
3 {
4 public static int X;
5 static A(){
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 Console.WriteLine("X={0},Y={1}",A.X,B.Y);
15 }
16 }
產生的輸出結果是什麼?
一般來說靜態聲明賦值語句先於靜態構造函數執行,沒有賦值的類成員聲明會被初始化成該類型的默認值,也就是說
public static int X;
比各自所在的靜態構造函數先執行,前一句X沒有賦值,默認就是0,後一句的Y在沒有賦值之前也是0,賦值後就是A.X+1的值。
public static int Y=A.X+1;
類的靜態初始化包括成員變量的聲明賦值,靜態構造函數的執行。
靜態初始化只有在類第一次被訪問的時候才執行,而且是優先於第一次訪問該類的代碼執行
因為Main函數在class B中,所以程序先執行的是上面的第二條語句,聲明一個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了 因此最終輸出的結果就是X=1,Y=2
對於引用類型成員的初始化說了這麼多還是總結一下吧.C#中初始化變量(包括實例成員變量和靜態成員變量)可以采用成員聲明的地方賦值的方式,也可以采用構造函數的方式.我個人在使用實例對象的時候比較推薦采用構造函數的方式,因為構造函數賦值的方式執行的順序是從父類到子類,這種順序避免了子類成員變量的初始化過程引用了未賦值的父類成員變量.而且在構造函數中初始化變量可以采用更多的語句塊,更多的判斷邏輯來初始化,甚至可以加上結構化異常處理try{}catch{}來處理異常信息,遠比單單一個賦值語句來得靈活.不過對於簡單的內置基本類型(如int,Enum,string等)就無所謂在哪裡進行初始化了.
以上是引用類型的初始化過程,值類型(這裡主要是指的結構類型)的靜態初始化和引用類型的完全一致.C#的結構類型是有構造函數的(記得C++裡面結構也貌似可以聲明構造函數),而實例成員的初始化因為結構沒有派生的功能,所以在這方面反而比較簡單.但是因為值類型始終是不能為空的,一旦聲明就必須要分配相應的內存空間,有了內存空間當然是要首先進行初始化的了,這都是為了保證值類型的有效性吧.這個過程是由Framework來完成的,我們自己是沒有辦法寫代碼來控制.因此Framework自己在初始化調用構造函數的時候當然就需要對自己要調用的構造函數的參數作個統一的約定,最簡單的就是無參構造函數了.所以在C#的每個結構裡都默認隱含了一個無參的構造函數,程序員自己可以重載構造函數,但是不能聲明自己的無參構造函數(這個是被Framework占用了的).
有很多剛從C++轉到C#的程序員在使用引用類型作為函數的臨時變量的時候還能認識到在使用之前需要new一下創建實例再使用,但是在使用結構作為函數的臨時變量的時候就喜歡聲明後直接拿來使用,問起他們的時候總是說結構是值類型,值類型是存在棧上的,聲明後就直接可以使用了.先不論這句話是不是正確的(關於C#中值類型和引用類型到底存在什麼地方有時間以後一定寫一篇文章專門討論一下).首先按C#編程規范值類型同樣是需要進行成員變量的封裝的,很多值類型在聲明後就不能夠改變,而只聲明一個結構體不賦值的話相當於是調用的默認的構造函數,而通常這個默認的構造函數對於我們來說是沒有什麼意義的.所以得到的值也是沒有太大的用處,除非你是想用作out參數所實參,真正用到的時候還得另外賦值.所以當你這樣使用結構體的時候,C#編譯器會警告你,這個變量只是聲明了沒有賦值(其實是相當於有一個值,但是沒有意義).其實變量使用之前賦值這也是一個很好的習慣,C++裡面雖然直接聲明了就可以用,但是一般也會在使用之前先ZeroMemory一下,這其實也是相當於初始化了結構體吧,唯一的區別是不需要重新分配空間.