C# 例項化的執行順序
首先進行細分
1.類的成員分為:欄位,屬性,方法,建構函式
2.成員修飾符:靜態成員,例項成員
不考慮繼承的關係執行順序為
1.靜態欄位
2.靜態構造方法
3.例項欄位
4.例項構造方法
其中 屬性和方法只有在呼叫的時候才執行。
下面是一段程式碼例子
定義了一個類,當我們例項化的時候
Test test = new Test();
通過VS2008模擬 執行過程是
0. 靜態的最先執行,模擬編譯略過
1. F11 執行 public int mNum2=2; //簡單的初始化欄位
2. private int mNum3 = 3;
private int mNum4 = 4;
3. public Test() { temp = 1;mNum = 0; } //建構函式
以上是執行的順序:
與上面描述的的執行過程穩和,屬性和欄位跳過。public int mNum;也執行了,只不過系統預設賦值為空(null)或者0,系統西東除了的模擬時候跳過。
class Test
{
public static int temp;
public int mNum;
public int mNum2=2;
public void look()
{
Console.WriteLine("看我,我是test" );
Console.WriteLine("mNum={0}", mNum);
}
private int mNum3 = 3;
private int mNum4 = 4;
public int Num3
{
get { return mNum3; }
set { mNum3= value; }
}
public Test()
{
temp = 1 ;
mNum = 0;
}
}
當存在繼承關係的時候,執行的順序。
1. 子類的靜態欄位
2. 子類的靜態構造方法
3. 子類的例項欄位
4. 父類的靜態欄位
5. 父類的靜態構造方法
6. 父類的例項欄位
7. 父類的例項構造方法
8. 子類的例項構造方法
下面是例子
1. 首先執行 class A中 int x = 1;,執行所有已經初始化後的欄位,當執行到建構函式首執行父類欄位
2. int sd = 6;
3. public A(),首先會去執行父類。
4. 0. 靜態的最先執行,模擬編譯略過
1. F11 執行 public int mNum2=2; //簡單的初始化欄位
2. private int mNum3 = 3;
private int mNum4 = 4;
3. public Test() { temp = 1;mNum = 0; } //建構函式
5. public A()
{
PrintFields();
look();
}// 執行子類中自己的建構函式
6. public virtual void PrintFields() ;執行自己類中有意義的虛擬函式
7. 執行呼叫父類的函式 look();
8. 接下來就將要執行 class B 中的內容了。過程類似,先一步一步的執行A類,在執行Test類,實現繼承。
9. 忽略部分細節,降調B類中的細節。
int z=2;
public B() 執行到這裡的時候,再次執行到
public A()
{
PrintFields();
look();
}
的建構函式的時候,由於執行的是例項化B類,又在B類中重寫了PrintFields();,實現多型。所以執行 public override void PrintFields()
{
Console.WriteLine("x={0},y={1}", "b", z);
}
所以此時的 z=2;
而不是 public B()
{
z = -1;
}
中的z=-1,因為還沒有執行到它,隨後才執行子類中的建構函式。
10. 接下來是執行C類的例項化,C類中沒有寫C的建構函式,但是還是執行到了
B類(它的父類)中int z=2;,是因為,預設給寫了無參的C類建構函式。
相當與 public C(){}
11. 所以任然能夠繼承,執行 public override void PrintFields()
{
Console.WriteLine(“j={0},k={1}”, “CC”, y);
}
class A :Test
{
int x = 1;
public A()
{
PrintFields();
look();
}
int c;
int sd = 6;
public virtual void PrintFields()
{
Console.WriteLine("j={0},k={1}", "AA", "AA");
}
}
class B : A
{
int y;
int z=2;
public B()
{
z = -1;
}
public override void PrintFields()
{
Console.WriteLine("x={0},y={1}", "b", z);
}
}
class C : B
{
int x = 1;
int y = 3;
public override void PrintFields()
{
Console.WriteLine("j={0},k={1}", "CC", y);
}
}
static void Main(string[] args)
{
Test test = new Test();
A a = new A();
B b = new B();
C c = new C();
Console.ReadKey();
}
補充說明
- 並不是每次例項化都是上面的順序。因為靜態的成員只是在第一次例項化的時候執行,以後再例項化都不會在執行。靜態的成員意味著大家共享,且只有這一個。第一次例項化得到靜態成員後,以後大家都共享,再次例項化,沒有必要也不允許執行靜態成員的部分。
- 在類中如果不新增訪問修飾符,預設是靜態的
- 非靜態的欄位,屬性,方法,不能夠作為右值。
- 構造引用型別的物件時,呼叫例項構造方法之前,為物件分配的記憶體總是先被歸零,構造器沒有顯式重寫欄位,欄位的值為0或者null,例如class Test 中的
public int mNum;mNum 預設為0 - C#編譯器提供了簡化語法,允許在變數定義的時候就進行初始化(C++應該不行,在構 造函式彙總)但是存在了程式碼膨脹的問題。多個欄位在定義時初始化,同時存在多個構造方法,每個構造方法都會把這些欄位初始化的程式碼搬到自己的內部,這樣造成程式碼的膨脹。
- 為了避免這樣情況,可以把這些欄位的初始化放到一個無參構造方法內,其他的構造方法顯式呼叫無參構造方法。
- 初始化欄位的兩種方法
①使用簡化語法:在定義的時候初始化
② 在構造方法內初始化。
效果圖