一、構造方法 類的構造方法是類的成員方法的一種,它的作用是對類中的成員進行初始化操作。類的構造方法分為: 1.靜態構造方法 2.實例構造方法 1.靜態構造方法 類的靜態構造方法是類的成員方法的一種,它的作用是對類中的靜態成員進行初始化操作。下麵請看代碼實例: 1 using System; 2 na ...
一、構造方法
類的構造方法是類的成員方法的一種,它的作用是對類中的成員進行初始化操作。類的構造方法分為:
1.靜態構造方法
2.實例構造方法
1.靜態構造方法
類的靜態構造方法是類的成員方法的一種,它的作用是對類中的靜態成員進行初始化操作。下麵請看代碼實例:
1 using System; 2 namespace LycheeTest { 3 class Test { 4 //定義一個靜態成員變數 5 private static int a; 6 //定義靜態構造函數 7 static Test() { 8 //初始化靜態成員變數 9 a = 11; 10 } 11 public void Show() { 12 Console.WriteLine("靜態欄位 a 的值是:{0}", a); 13 14 } 15 } 16 class Program { 17 static void Main(string[] args) { 18 Test t = new Test(); 19 t.Show(); 20 Console.ReadKey(); 21 } 22 } 23 }
首先,上面這段代碼定義了兩個類。第 3 行代碼定義了類 Test。定義類的時候,類的訪問許可權修飾符有兩個,一個是 public,另一個是 internal。當不寫任何訪問修飾符的時候,類的訪問許可權預設是 internal。 這個訪問許可權的意義是,這個類只能被本程式集訪問,不能被本程式集以外的類訪問。如果這個類是屬於類庫的,那麼它必須是 public 的,否則調用它的程式集就不能訪問它。第 5 行代碼定義了一個靜態欄位成員,第 7 行代碼就是靜態構造方法。可以看到,靜態構造方法的特點是,以 static 關鍵字說明這個方法是靜態的,方法名稱要和類名完全相同,這裡要註意大小寫。靜態構造方法不能含有參數,靜態構造方法不能有返回值。在靜態構造方法體內可以做初始化靜態成員的操作。第 11 行代碼定義了一個實例方法,它的作用是輸出靜態欄位的值。在第 16 行的類 Program 中的 Main(註意:在java中是main) 方法中調用了這個類,第 18行創建了這個類的一個實例,第 19 行 調用了類的實例方法。
從上面的代碼中可以看到,並沒有顯式調用類的靜態構造方法。
下麵請看以上代碼的執行結果:
靜態欄位 a 的值是:11
可以看到,靜態構造方法確實是被執行了。那麼上例就是靜態構造方法的執行條件之一,在類的實例被創建時,類的靜態構造方法將被自動調用。
靜態構造方法的調用次序是在靜態欄位的初始值設定項之後。
也就是第一步是靜態欄位的預設值設置,第二步是執行靜態欄位的初始值設定項,第三步就是調用類的靜態構造方法。
下麵將前面的代碼實例修改一下,代碼如下:
1 using System; 2 namespace LycheeTest{ 3 class Test { 4 private static int a; 5 static Test() { 6 a++; 7 } 8 public void Show() { 9 Console.WriteLine("靜態欄位 a 的值是:{0}", a); 10 14 11 } 12 } 13 class Program { 14 static void Main(string[] args) { 15 Test t = new Test(); 16 t.Show(); 17 Test t1 = new Test(); 18 t.Show(); 19 Console.ReadKey(); 20 } 21 } 22 }
這段代碼將靜態構造方法做了修改,在方法體內將靜態欄位 a 進行自增操作。然後在代碼的第 17 行又 創建了一個類的實例,然後再次調用類的實例方法。
下麵看執行結果:
靜態欄位 a 的值是:1 靜態欄位 a 的值是:1
可以看到,靜態欄位的值並沒有增加。這就是靜態構造方法執行的特點,它只執行了一次。當程式集 運行的時候,將會創建一個應用程式域,在一個應用程式域中,類的靜態構造方法僅僅執行一次。
下麵再對代碼實例進行修改如下:
1 using System; 2 namespace LycheeTest { 3 class Test { 4 public static int a; 5 static Test() { 6 Console.WriteLine("類的靜態構造方法開始執行"); 7 a++; 8 } 9 public void Show() { 10 Console.WriteLine("靜態欄位 a 的值是:{0}", a); 11 } 12 } 13 class Program { 14 static void Main(string[] args) { 15 Console.WriteLine("靜態欄位 a 的值是:{0}", Test.a); 16 Console.WriteLine("靜態欄位 a 的值是:{0}", Test.a); 17 Console.ReadKey(); 18 } 19 } 20 }
這段代碼在類的靜態構造方法中列印輸出了一行標記,類的靜態欄位的訪問許可權也修改為 public,這讓它可以在類外被調用。在 Main 方法中兩次列印輸出了靜態欄位的值,註意在類外調用類的靜態欄位需要 使用類名進行引用。
下麵是代碼的執行結果:
類的靜態構造方法開始執行 靜態欄位 a 的值是:1 靜態欄位 a 的值是:1
本段代碼並沒有創建類的實例。在引用類的靜態成員之前,類的靜態構造方法將被調用。這個被調用的類的靜態成員包括靜態欄位和靜態方法。這就是類的靜態構造方法調用的第二個條件。
下麵再對代碼實例進行修改如下:
1 using System; 2 namespace LycheeTest { 3 class Program { 4 private static int a; 5 static Program() { 6 Console.WriteLine("類的靜態構造方法被調用"); 7 a = 11; 8 } 9 static void Main(string[] args) { 10 Console.WriteLine("Main 方法被調用"); 11 Console.WriteLine("靜態欄位 a 的值是:{0}", a); 12 Console.ReadKey(); 13 } 14 } 15 }
這段代碼在包含 Main 方法的類中定義了靜態欄位和靜態構造方法。因為 Main 方法也是一個靜態方法,類的靜態構造方法被調用而且它是類的入口點方法,那麼它和類的靜態構造方法之間是誰先調用呢?下麵首先來看代碼的執行結果:
類的靜態構造方法被調用 Main 方法被調用 靜態欄位 a 的值是:11
通過代碼的執行結果可以看到,因為類的入口點方法仍然是一個靜態方法,那麼在任何靜態成員被調用之 前,靜態構造方法都首先被調用。所以,可以得出如下結論,類的靜態構造方法先於類的 Main 方法被調用。
那麼類的靜態構造方法能否被顯式調用呢?下麵看代碼實例:
1 using System; 2 namespace LycheeTest { 3 class Program { 4 private static int a; 5 static Program() { 6 Console.WriteLine("類的靜態構造方法被調用"); 7 a = 11; 8 } 9 static void Main(string[] args) { 10 Program(); 11 Console.ReadKey(); 12 } 13 } 14 }
在這段代碼中的第 10 行顯式調用了類的靜態構造方法,這時編譯器會報錯。
2.實例構造函數
類的實例構造方法是類的成員方法的一種,它的作用是對類的實例成員進行初始化操作。實例構造方法可以實現重載,在創建類的實例時,可以顯式的指定不同的參數來調用重載的不同的實例構造方法。下麵請看代碼實例:
1 using System; 2 namespace LycheeTest { 3 class Program { 4 private static int a; 5 private int b = 12; 6 private string c = "Hello World"; 7 8 static Program() { 9 Console.WriteLine("類的靜態構造方法被調用"); 10 a = 11; 11 } 12 public Program(int a, string s) { 13 Console.WriteLine("帶二個參數的構造方法被調用"); 14 this.b = a; 15 this.c = s; 16 } 17 public Program(int a) : this(a, "通過 this 關鍵字調用構造方法") { 18 Console.WriteLine("帶一個參數的構造方法被調用"); 19 } 20 public void Show() { 21 Console.WriteLine("靜態欄位 a 的值是:{0}", a); 22 Console.WriteLine("實例欄位 b 的值是:{0}", b); 23 Console.WriteLine("實例欄位 c 的值是:{0}", c); 24 } 25 static void Main(string[] args) { 26 Program p1 = new Program(33, "這是創建的實例 P1"); 27 Program p2 = new Program(34); 28 p1.Show(); 29 p2.Show(); 30 Console.ReadKey(); 31 } 32 }
這段代碼的第 4 行、第 5 行和第 6 行分別定義了三個欄位成員,第 4 行是靜態欄位,第 5 行和第 6 行代碼都有初始值設定項。代碼的第 8 行就是一個實例構造方法的定義,實例構造方法也是以類名作為方法名,它沒有返回值, 在方法名前面是訪問許可權修飾符,可以使用的訪問許可權修飾符包括 public、private 和 protected。其中的 protected 意味著構造方法只能在此類內部訪問。實例構造方法可以帶參數。 第 12 行代碼的實例構造方法使用兩個傳入的參數對實例欄位進行了賦值。第 17 行代碼定義了帶一個參數的實例構造方法,它和前一個實例構造方法形成了重載。實例構造方法可以通過 this 關鍵字調用其他的實例構造方法,方法就是在參數列表的後面使用冒號然後接 this 關鍵字, 然後再跟參數列表,這個參數列表要匹配另一個重載的實例構造方法。第 17 行的構造方法只有一個參數, 它將這個參數通過 this 關鍵字傳遞給了另一個構造方法,在用 this 調用另一個構造方法的時候,為其同時傳入了一個字元串參數。第 24 行的實例方法列印類的欄位成員的值。在 Main 方法中,第 26 行代碼和第 27 行代碼分別定義了兩個實例,它們使用 new 關鍵字調用了不同的實例構造方法。第 28 行和第 29 行分別調用實例方法列印類的靜態欄位和實例的兩個欄位成員的值。
下麵先來看代碼的執行結果:
類的靜態構造方法被調用 帶二個參數的構造方法被調用 帶二個參數的構造方法被調用 帶一個參數的構造方法被調用
靜態欄位 a 的值是:11
實例欄位 b 的值是:33 實例欄位 c 的值是:這是創建的實例 P1 靜態欄位 a 的值是:11 實例欄位 b 的值是:34 實例欄位 c 的值是:通過 this 關鍵字調用構造方法
現在用執行結果來介紹實例構造方法的執行過程,當第 26 行代碼創建類的實例時,類的靜態欄位首先被設置成預設值,因為沒有欄位的初始值設定項,所以接著就執行類的靜態構造方法。這時靜態欄位 a 被 設置成 11。因為第 26 行代碼使用 new 調用了帶有兩個參數的實例構造方法,所以首先實例欄位 b 被設置為 0,實例欄位 c 被設置為 null。然後執行欄位的初始值設定項,b 被賦值為 12,c 被賦值為“Hello World”。接 下來執行實例構造方法體中的第一個語句,“帶二個參數的構造方法被調用”這個字元串被列印。接下來 實例 p1 的欄位 b 被設置為傳入的參數 33,註意構造方法的形參 a 在這裡覆蓋了類的靜態欄位 a。也就是說, 這時起作用的是實例構造方法的局部變數 a。然後實例欄位 c 被設置為字元串"這是創建的實例 P1"。第 27 行代碼使用 new 關鍵字調用了帶一個參數的實例構造方法,在調用時,首先屬於 p2 的實例欄位 b 被設置為 0,實例欄位 c 被設置為 null。然後執行欄位的初始值設定項,b 被賦值為 12,c 被賦值為“Hello World”。接下來執行的是 this 引用的帶兩個參數的實例構造方法,"帶二個參數的構造方法被調用"這個 字元串被列印。然後 b 被設置為 34,c 被設置為"通過 this 關鍵字調用構造方法"。最後,代碼控制又返回 來執行帶一個參數的實例構造方法體中的列印語句,"帶一個參數的構造方法被調用"這個字元串被列印。 至此,實例構造方法的執行完畢。接下來的代碼列印靜態欄位的值,可以看到兩個實例列印出來的靜態欄位值是一樣的,但是它們的實 例欄位的值各不相同。
可選參數和命名參數也可以用於實例構造方法,下麵看代碼實例:
1 using System; 2 namespace LycheeTest { 3 class Program { 4 private int b; 5 private string c; 6 public Program(int a = 12, string s = "") { 7 this.b = a; 8 this.c = s; 9 } 10 public void Show() { 11 Console.WriteLine("實例欄位 b 的值是:{0}", b); 12 Console.WriteLine("實例欄位 c 的值是:{0}", c); 13 } 14 static void Main(string[] args) { 15 Program p1 = new Program(); //構造方法的兩個參數都採用預設值 16 Program p2 = new Program(34); //構造方法的 string 類型參數採用預設值 17 Program p3 = new Program(23, "Hello World"); //構造方法的兩個參數採用傳入參數 18 Program p4 = new Program(s: "今天的天氣真好"); //採用命名參數,另一個參數 a 採用預設值 19 p1.Show(); 20 p2.Show(); 21 p3.Show(); 22 p4.Show(); 23 Console.ReadKey(); 24 } 25 } 26 }
代碼的第 6 行定義了一個帶有可選參數和命名參數的構造方法,然後第 15 創建了一個類的實例,在構造方法中沒有傳入任何參數,這時,構造方法的兩個參數都採用預設值。第 16 行代碼為構造方法傳入了一個 int 類型的參數,這時,另一個 string 類型的參數採用預設值。 第 17 行代碼傳入了兩個參數,構造方法的兩個參數都使用了這兩個傳入的參數。第 18 行代碼使用了命名參數指定傳入的參數是 string 類型的參數,並將它傳遞給形參 s。這時另一 個 int 類型的參數採用預設值。第 19 行到第 23 行代碼列印類的實例欄位的值。這段代碼的執行結果如下:
實例欄位 b 的值是:12 實例欄位 c 的值是: 實例欄位 b 的值是:34 實例欄位 c 的值是: 實例欄位 b 的值是:23 實例欄位 c 的值是:Hello World 實例欄位 b 的值是:12 實例欄位 c 的值是:今天的天氣真好