1. 建立物件
我們終於來到物件的建立啦。你之前應該已經碰過這個,只是現在我們要更仔細地聊聊。其實建立物件超簡單的。
要建立物件,你要用 new 運算子。通常你會建立一個 class 的新實例(物件),然後馬上把它存到同型別的變數裡。
類別 名稱 = new 類別(參數);
這裡 類別 是物件的型別,名稱 是變數名稱(通常小寫開頭,比如 cat),而 = 右邊就是建立這個 class 的新物件。
範例:
| 程式碼 | 說明 |
|---|---|
|
建立 object 型別的物件 |
|
建立 Cat 型別的物件 |
|
建立帶參數的 StreamReader 物件 |
很常見的寫法是,程式設計師會把變數名稱取成跟 class 一樣,只是小寫開頭。新手看到這種寫法可能會有點混亂:
| 程式碼 |
|---|
|
|
|
其實這種寫法完全沒問題——就是很普通的程式碼,建立一個變數,然後馬上用同型別的物件初始化它。
左邊是變數宣告,右邊是用 new 在記憶體裡建立新物件。就這樣!
2. 建構子
你應該常常看到,建立物件時會傳一些參數進去。有些物件要參數,有些不用。那這個參數機制到底怎麼運作?
這也很簡單:每個 class 都有一個(或多個)特別的方法,專門處理建立物件時的參數。這種方法叫 建構子。一個方法就是 建構子。
要分辨建構子跟一般方法很簡單。建構子有兩個特點:
- 建構子的名稱跟 class 名稱一樣(而且大寫開頭)
- 建構子沒有回傳型別(連 void 都不用寫)。
一般語法長這樣:
修飾詞 類別(參數)
{
建構子程式碼
}
建構子宣告範例:
public class Point
{
public int X;
public int Y;
public Point(int x, int y) // 建構子
{
X = x;
Y = y;
}
}
用法:
Point p = new Point(5, 10); // 呼叫建構子
用 new Point(5, 10) 建立物件時,會呼叫帶兩個參數的建構子,初始化 X 跟 Y 欄位。
有時候建構子的參數名稱會跟欄位一樣——這樣比較方便。這種名稱衝突可以用 關鍵字 this 解決:
public Point(int x, int y)
{
this.x = x; // this.x 是物件欄位,x 是建構子的參數
this.y = y;
}
3. 呼叫建構子
當你寫 new 類別(參數),C# 會:
- 在記憶體裡建立新物件,
- 呼叫對應的建構子,把你的參數傳進去。
以貓咪 class 為例:
public class Cat
{
public string Name;
public int Age;
public Cat(string name, int age)
{
Name = name;
Age = age;
}
}
用法:
Cat cat = new Cat("瓦斯卡", 2); // OK:呼叫帶兩個參數的建構子
Cat cat2 = new Cat("瓦斯卡"); // 不行:沒有這種建構子——編譯錯誤
Cat cat3 = new Cat(); // 不行:沒有無參數建構子——編譯錯誤
如果 class 只有帶參數的建構子,你就必須在建立物件時給它參數。
4. 多個建構子
不過如果你想,也可以在 class 裡加很多個建構子。建構子的數量跟參數沒有限制。建立物件時,編譯器會自動挑對應參數的建構子。
範例:
public class Cat
{
public const int UNKNOWN = -1;
public string Name;
public int Age;
public Cat(string name, int age)
{
Name = name;
Age = age;
}
public Cat(string name)
{
Name = name;
Age = UNKNOWN; // 年齡未知時的預設值
}
}
用法:
Cat cat1 = new Cat("瓦斯卡", 2); // 用第一個建構子
Cat cat2 = new Cat("穆卡"); // 用第二個建構子
Cat cat3 = new Cat(); // 錯誤!沒有無參數建構子
我們考慮到有時候貓的年齡可能不知道。這時就加了一個常數 UNKNOWN,還有一個只需要名字的建構子。
注意,我們還是會在兩個建構子裡初始化所有 class 變數。缺的參數就用 UNKNOWN 代替。
5. 預設建構子
如果你的 class 沒有任何建構子,編譯器會自動加上一個空的無參數建構子:
public ClassName()
{
}
但如果 class 裡已經有自己宣告的建構子——就不會自動加空建構子了。
範例 1:
public class Cat
{
public string Name;
public int Age;
}
// 可以:
Cat cat = new Cat(); // 會呼叫預設建構子(無參數)
範例 2:
public class Cat
{
public string Name;
public int Age;
public Cat(string name) { Name = name; }
}
// 不行:
Cat cat = new Cat(); // 錯誤:沒有無參數建構子了!
6. 變數初始化
在 C# 裡,class 變數可以直接在宣告時初始化,也可以在建構子裡初始化。
範例:
public class Cat
{
public string Name;
public int Age = -1; // 初始值
public Cat(string name, int age)
{
Name = name;
Age = age; // 覆蓋初始值
}
public Cat()
{
Name = "無名";
// Age 還是 -1(初始值)
}
}
說明:
- 用 new Cat("瓦斯卡", 2) 建立時,Age 先是 -1,但建構子會把它改成 2。
- 用 new Cat() 建立時,Age 會保持 -1。
7. 變數初始化順序
新物件的資料初始化順序如下:
- class 欄位 先拿到預設值(0 給 int,null 給參考型別等等)。
- 然後執行你在欄位宣告時寫的初始化器,照程式碼出現順序。
- 最後才執行建構子的程式碼。
變數會在建構子之前、照宣告順序初始化。所以欄位之間有依賴關係時要小心。
錯誤範例
非 static 欄位不能用其他非 static 變數初始化:
public class Solution
{
public int a = 1;
public int b = a; // 不能用 a
public int c = a + b; // 不能用 a 跟 b
}
// 這樣會編譯錯誤!
正確範例
對 static 變數來說,這樣就沒問題:
public class Solution
{
public static int a = 1; // 明確設定 a = 1
public static int b = a + 2; // b = 1 + 2 ⇒ 3
public static int c = a + b + 3; // c = 1 + 3 + 3 ⇒ 7
public Solution()
{
Console.WriteLine($"a = {a}, b = {b}, c = {c}");
// 會印出:a = 1, b = 3, c = 7 —— 就是預期的結果
}
}
正確範例 2
static 欄位甚至可以這樣寫:
public class Solution
{
public static int a = b + c + 1; // b 跟 c 還是 0
public static int b = a + c + 2; // a == 1, c 還是 0
public static int c = a + b + 3; // a == 1, b == 3
}
GO TO FULL VERSION