C# 構造函數總結
構造函數
構造函數分為:實例構造函數,靜態構造函數,私有構造函數。
實例構造函數
1、構造函數的名字與類名相同。
2、使用 new 表達式創建類的對象或者結構(例如int)時,會調用其構造函數。並且通常初始化新對象的數據成員。
3、除非類是靜態的,否則會為沒有構造函數的類,自動生成一個默認構造函數,並使用默認值來初始化對象字段。
4、構造函數可以有參數,可以以多態的形式存在多個構造函數。
例:
class CoOrds { public int x, y; // 實例構造函數(默認構造函數) public CoOrds() { x= 0; y = 0; } // 具有兩個參數的構造函數 public CoOrds(int x, int y) { this.x = x; this.y = y; } // 重寫toString方法 public override string ToString() { return (String.Format("({0},{1})", x, y)); } static void Main(string[] args) { CoOrds p1= new CoOrds(); CoOrds p2 = new CoOrds(5, 3); // 使用重寫ToString方法顯示結果 Console.WriteLine("CoOrds #1 at {0}", p1); Console.WriteLine("CoOrds #2 at {0}", p2); Console.ReadKey(); } } /* Output: CoOrds #1 at (0,0) CoOrds #2 at (5,3) */
其中CoOrds()是構造函數,諸如此類不帶參數的構造函數稱為“默認構造函數”。
CoOrds(int x, int y)同樣也是構造函數,構造函數可以有參數,允許多態。
靜態構造函數
靜態構造函數具有以下屬性:
-
靜態構造函數不使用訪問修飾符或不具有參數。
-
在創建第一個實例或引用任何靜態成員之前,將自動調用靜態構造函數以初始化類。
-
不能直接調用靜態構造函數。
-
用戶無法控制在程序中執行靜態構造函數的時間。
-
靜態構造函數的一種典型用法是在類使用日誌文件且將構造函數用於將條目寫入到此文件中時使用。
-
靜態構造函數對於創建非托管代碼的包裝類也非常有用,這種情況下構造函數可調用
LoadLibrary
方法。 -
如果靜態構造函數引發異常,運行時將不會再次調用該函數,並且類型在程序運行所在的應用程序域的生存期內將保持未初始化。
構造函數與靜態構造函數:
class TestClass { public static int x = 0; //構造函數 TestClass() { x = 1; } //靜態構造函數 static TestClass() {
//第二步,執行x = 2 x = 2; }
//第一步,程序入口Main最先執行。然後執行public static int x = 0 接著執行靜態構造函數。 public static void Main(string[] args) { Console.WriteLine("x:{0}", x); //打印,x = 2 TestClass Test = new TestClass();//第三步執行構造函數,此時x = 1 Console.WriteLine("x:{0}", x); //打印 x = 1 Console.Read(); } }
Main是程序入口,當執行Main的時候,最先執行public static int x = 0
接著執行靜態構造函數,此時 x = 2
然後執行Main函數裏面的內容,打印 x,此時 x = 2
初始化TestClass,然後會執行構造函數,此時 x = 1
打印 x = 1
那麽,在調用某類的靜態函數時真正的執行順序:
1、靜態變量 > 靜態構造函數 > 靜態函數
2、靜態變量 > 靜態構造函數 > 構造函數
C#高效編程改進C#代碼的50個行之有效的辦法(第2版)裏說到這樣一段話:類型實例的完整過程。你需要理解這些操作的順序,以及對象的默認初始化操作。你要保證在構造的過程中對每個成員變量僅初始化一次。實現這一點最好的方法就是,盡可能的早地進行初始化。
下面就是創建某個類型的第一個實例時,所進行的操作順序為:
(1)靜態變量設置為0
(2)執行靜態變量初始化器
(3)執行基類的靜態構造函數
(4)執行靜態構造函數
(5)實例變量設置為0
(6)執行衯變量初始化器
(7)執行基類中合適的實例構造函數
(8)執行實例構造函數 同樣類型的第二個以及以後的實例將從第5步開始執行,因為類的構造器僅會執行一次。此外,第6步和第7步將被優化,以便構造函數初始化器使編譯器移除重復的指令。
練習題:
public class A { public static readonly int x; static A() { x = B.y + 1; } } public class B { public static int y = A.x + 1; public static void Main(string[] args) { Console.WriteLine("x:{0},y:{1}。", A.x, y); Console.ReadLine(); } }
下面公布答案:
public class A { public static readonly int x; static A() { //第二步,調用B.y,此處B.y = 0,因為int類型在初始化階段,會給賦默認值,默認值為0。最後x = 0 + 1(返回給第一步) x = B.y + 1; } } public class B { //第一步,調用A.x,然後執行類A的靜態構造函數,等待返回(第二步返回的A.x = 1,所有y = 1 + 1) public static int y = A.x + 1; public static void Main(string[] args) { //第三步,A.x = 1,y = 2。 Console.WriteLine("x:{0},y:{1}。", A.x, y); Console.ReadLine(); } }練習題答案
詳細解答:
1、首先,每一個項目有且只能有一個靜態類的Main函數作為入口函數。而入口函數是最先執行的。
2、由於Main函數在B類裏面,首先會初始化B類。而類的初始化順序是:類裏的靜態變量,然後執行靜態構造函數。
3、運行起先執行 public static int y = A.x + 1 這個,執行的時候,會先把 y 初始化為0,然後計算 y 的值。
4、計算 y 的值的時候,調用了 A 的靜態變量 x 。所以會先初始化A。
5、初始化A時首先去執行 public static readonly int x ,先把 x 初始化為0。
6、然後執行A的靜態構造函數 x = B.y + 1 此時 y 已經初始化為0了。
7、計算得到 x = 1。然後回到 public static int y = A.x + 1 得到 y = 2。
8、然後再執行Main函數的內容。得出結果x=1,y=2
私有構造函數
私有構造函數是一種特殊的實例構造函數。 它通常用於只包含靜態成員的類中。 如果類具有一個或多個私有構造函數而沒有公共構造函數,則其他類(除嵌套類外)無法創建該類的實例。
public class PrivateConstructor { private PrivateConstructor() { //PrivateTest a = new PrivateTest(); //註釋打開會報錯,錯誤信息:不可訪問,因為它受保護級別限制。因為私有構造函數無法在類的外面實例化。 } public class PrivateTest { int i; private PrivateTest() { i = 3; } static void Main(string[] args) { PrivateConstructor t = new PrivateConstructor(); //嵌套類允許實例化。 PrivateTest p = new PrivateTest(); //類的內部允許實例化。 Console.WriteLine("i:{0}", p.i); //結果:i:3 Console.Read(); } } }
聲明空構造函數可阻止自動生成默認構造函數。 請註意,如果不對構造函數使用訪問修飾符,則在默認情況下它仍為私有構造函數。 但是,通常會顯式地使用 private 修飾符來清楚地表明該類不能被實例化。
實例:
其中單例模式就用到了私有構造函數的特性來保證類不會被實例化。C# 單例模式
相關文章: http://www.cnblogs.com/michaelxu/archive/2007/03/29/693401.html
https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/classes-and-structs/constructors
C# 構造函數總結