構(gòu)造函數(shù)的最基本的作用是為類型的一個新的實例中所有的字段和屬性分配初始值。所以,根據(jù)其功能,他不需要(也沒有意義)返回值。他的函數(shù)名必須和類名相同。
任何時候,只要創(chuàng)建類或結(jié)構(gòu)的一個實例,就會調(diào)用它的構(gòu)造函數(shù)。類或結(jié)構(gòu)可能有多個接受不同參數(shù)的構(gòu)造函數(shù)。構(gòu)造函數(shù)使得程序員可設(shè)置默認(rèn)值、限制實例化以及編寫靈活且便于閱讀的代碼。
如果沒有為對象提供構(gòu)造函數(shù),則默認(rèn)情況下 C# 將創(chuàng)建一個沒有任何參數(shù)的構(gòu)造函數(shù),該構(gòu)造函數(shù)將會調(diào)用其基類的無參數(shù)的構(gòu)造函數(shù)。如果基類也沒有則繼續(xù)上溯,直到object的公共構(gòu)造函數(shù),他什么都不做。(通常對于所有的值類型,上溯到system.valuetype下對應(yīng)的類型例如int32等,然后為值類型賦0,對于所有的引用類型,賦null)
如果我們顯式的寫出了任何一個構(gòu)造函數(shù)(不管是否有輸入?yún)?shù)),c#都不會再為我們創(chuàng)建那個沒有參數(shù)的構(gòu)造函數(shù)了。
構(gòu)造函數(shù)不能被繼承。不能在構(gòu)造函數(shù)前面加abstract, virtual, new, sealed,override。
構(gòu)造函數(shù)是方法,所以也可以有存取修飾詞。一般來說,構(gòu)造函數(shù)都是PUBLIC的。但如果我們設(shè)定其為PRIVATE(私有構(gòu)造函數(shù)),那么他就不能被外部訪問。所造成的效果就是該類不能被實例化。私有構(gòu)造函數(shù)是一種特殊的實例構(gòu)造函數(shù)。它通常用在只包含靜態(tài)成員的類中。如果類具有一個或多個私有構(gòu)造函數(shù)而沒有公共構(gòu)造函數(shù),則其他類(除了嵌套類)無法創(chuàng)建該類的實例。
構(gòu)造函數(shù)之間可以通過this調(diào)用默認(rèn)構(gòu)造函數(shù)(沒有參數(shù)的那個)。通常來說,如果這么做(構(gòu)建構(gòu)造函數(shù)鏈),則默認(rèn)構(gòu)造函數(shù)只能有一個,他的參數(shù)沒有限制。但只能有一個的原因是所有構(gòu)造函數(shù)的名字都必須相同,所以如果超過一個,this將不知道要調(diào)用哪個。
子類若不顯式的調(diào)用父類的構(gòu)造函數(shù)時,編譯器會自動調(diào)用父類的默認(rèn)(無參)構(gòu)造函數(shù)。可以用Base關(guān)鍵字指明要調(diào)用父類的哪個構(gòu)造函數(shù)。
練習(xí):以下代碼輸出什么?
public class MyBaseClass { public MyBaseClass() { Console.WriteLine("In MyBaseClass()"); } public MyBaseClass(int i) { Console.WriteLine("In MyBaseClass(" + i + ")"); } } public class MyDerivedClass : MyBaseClass { public MyDerivedClass() { Console.WriteLine("In MyDerivedClass()"); } public MyDerivedClass(int i) { Console.WriteLine("In MyDerivedClass(" + i + ")"); } //public MyDerivedClass(int i, int j) //{ // Console.WriteLine("In MyDerivedClass(int i,int j)"); //} public MyDerivedClass(int i, int j) : base(i) { Console.WriteLine("In MyDerivedClass(" + i + ",int " + j+ "):base(" + i + ")"); } } class Program { static void Main(string[] args) { //Event1 MyDerivedClass myObj1 = new MyDerivedClass(); Console.WriteLine(); //Event2 MyDerivedClass myObj2 = new MyDerivedClass(4); Console.WriteLine(); //Event3 MyDerivedClass myObj3 = new MyDerivedClass(4,8); Console.WriteLine(); Console.ReadKey(); } }解答:
Event1:實例化myObj1,自動調(diào)用父類(再往上就是system.object,其構(gòu)造函數(shù)什么都不做,下同)的無參構(gòu)造函數(shù),和自己的無參構(gòu)造函數(shù)。
Event2:實例化myObj2,自動調(diào)用父類的無參構(gòu)造函數(shù),和自己的有參構(gòu)造函數(shù)。
Event3:實例化myObj3,根據(jù)base關(guān)鍵字,顯式的調(diào)用父類的有參構(gòu)造函數(shù),和自己的有參構(gòu)造函數(shù)。
輸出:
In MyBaseClass()
In MyDerivedClass()
In MyBaseClass()
In MyDerivedClass(4)
In MyBaseClass(4)
In MyDerivedClass(4,int 8):base(4)
值類型的構(gòu)造函數(shù)可以存在,但系統(tǒng)永遠不會自動的調(diào)用。我們必須顯式的調(diào)用值類型的構(gòu)造函數(shù)(例如結(jié)構(gòu)體的)。另外,C#不允許定義值類型的無參數(shù)構(gòu)造器。
對于類型中的非靜態(tài)字段,類型的每一個對象都會維護字段的獨立副本。相對而言,對于靜態(tài)字段,所有該類型的實例共享一個值。如果要定義一個所有對象都可以分享的數(shù)據(jù)點,就可以考慮使用靜態(tài)成員。
假設(shè)類中有若干個靜態(tài)的字段,在構(gòu)造函數(shù)中,我們初始化他們的值,這時,假設(shè)我們在外部方法中更改了靜態(tài)字段的值,然后實例化一個新的類,新的類中靜態(tài)字段的值會被重置為初始化的值,如果我們不希望如此而是要保持靜態(tài)成員的值不變,就要借助靜態(tài)構(gòu)造函數(shù)。靜態(tài)構(gòu)造函數(shù)只會執(zhí)行一次(在第一個該類型的實例被創(chuàng)建的時候),之后,無論再創(chuàng)建多少該類型的實例,都不會再次運行,這保證了類型中的靜態(tài)成員的值不受影響。
靜態(tài)構(gòu)造函數(shù)具有以下特點:
下例選自CLRvia c#, 下面代碼輸出什么?
class Program { static void Main(string[] args) { B b = new B(); Console.ReadKey(); } } class A { public A(string text) { Console.WriteLine(text); } } class B { static A a1 = new A("a1"); A a2 = new A("a2"); static B() { a1 = new A("a3"); } public B() { a2 = new A("a4"); } }答案:
a1
a3
a2
a4
解釋:a2和a4肯定在后面這個較好理解,因為靜態(tài)構(gòu)造函數(shù)先于其他構(gòu)造函數(shù)執(zhí)行。當(dāng)創(chuàng)建一個新的B的實例時,先執(zhí)行的是類中和靜態(tài)有關(guān)的語句,然后便是靜態(tài)構(gòu)造函數(shù),所以a1先于a3打印出來。下面一個例子可以看得更清楚:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 B b = new B(); 6 Console.ReadKey(); 7 } 8 } 9 10 class A11 {12 public A(string text)13 {14 Console.WriteLine(text);15 } 16 }17 18 class B19 {20 static A a1 = new A("a1");21 static int aProperty = 1;22 int bProperty = 2;23 A a2 = new A("a2");24 25 static B()26 {27 a1 = new A("a3");28 }29 30 public B()31 {32 a2 = new A("a4");33 }34 }假如我們開啟調(diào)試模式的話,那么代碼(按照行號)的運行順序?qū)⑹菑男?9開始,然后20-13至15-21-(并不會運行非靜態(tài)的部分)25-26-27-13至15-28(靜態(tài)部分結(jié)束)-22-23-13至15-31-32-13至15-33-6-結(jié)束。
靜態(tài)構(gòu)造函數(shù)只會執(zhí)行類中和靜態(tài)有關(guān)的語句(先初始化類中和靜態(tài)有關(guān)的變量,再執(zhí)行靜態(tài)函數(shù)語句)。靜態(tài)構(gòu)造函數(shù)只會執(zhí)行一次。靜態(tài)構(gòu)造函數(shù)的執(zhí)行契機為初始化該類型之前。
下面代碼輸出什么?
1 public class A 2 { 3 public static readonly int x; 4 static A() 5 { 6 x = B.y + 1; 7 } 8 } 9 10 class B11 {12 public static int y = A.x + 1;13 14 static void Main(string[] args)15 {16 Console.WriteLine("x:{0},y:{1}。", A.x, y);17 Console.ReadLine();18 }19 }此題非常詭異,乍一看好像無法運行,讓我們慢慢分析。首先程序從類B開始運行,然后程序發(fā)現(xiàn),類B擁有一個靜態(tài)的成員y,于是初始化之,而又沒有靜態(tài)構(gòu)造函數(shù),于是先將其初始化為0。(可以將第12行拆成兩行看,第一行是public static int y,第二行是y = A.x + 1)。此時y=0,然后令y等于A.x+1,程序又不知道A是啥,于是進入A類,初始化A的靜態(tài)成員x為0,然后執(zhí)行A的靜態(tài)構(gòu)造函數(shù),此時因為B.y為0,所以可以順利的將x設(shè)置為0+1=1。
此時程序離開A類,回到第12行,y=1+1=2,最后,打印出來結(jié)果,x=1,y=2。
|
新聞熱點
疑難解答