1. 簡單建構子
建構子 — 就是 class 裡一個特別的方法,在你 new 物件時會自動被呼叫。它會初始化(設定)物件:給初始值、做檢查,甚至你想寫個笑話讓你心情好也可以。
建構子讓你可以直接把物件生出來就有想要的狀態,不用一個一個 field 或 property 慢慢手動指定。這樣很方便又安全,因為你一開始就把重要參數都給好 — 不會忘記什麼超重要的東西。
沒有建構子的話,code 可能會很冗長又容易出錯:
Person p = new Person();
p.Name = "Юля";
p.Age = 25; // 如果忘記指定年齡會怎樣?
有建構子的話:
Person p = new Person("Юля", 25); // 一次給好,物件直接 ready to go!
建構子的語法
建構子就是一個特別的「方法」,名字要跟 class 名完全一樣,而且沒有回傳值(連 void 都不能有)。這是預設建構子的寫法:
public class Person
{
// 預設建構子(沒參數)
public Person()
{
// 這裡可以寫初始化的 code
}
}
預設建構子就像一個隱形「小幫手」,不收任何參數,直接把 class 裡的 field 跟 property 都設成預設值:
- 數字型別(int、double、float 等等)會是 0。
- 布林型別(bool)會是 false。
- 參考型別(string、其他 class)會是 null(就是「什麼都沒有」、「沒有指到物件」的意思)。
- DateTime 會是 01.01.0001 00:00:00。
怎麼呼叫建構子?
其實你在 new 物件時寫的 new,就是在呼叫建構子啦!
Person p = new Person();
如果你沒自己寫建構子,C# 編譯器會幫你生一個預設建構子(沒參數的那種)。但只要你自己寫了一個,預設建構子就不見了,編譯器會叫你自己加回來(如果你還需要的話)。
範例
來加個建構子到我們的應用程式 class。這是學生 class 的簡單版:
public class Student
{
public string Name;
public int Age;
// 兩個參數的建構子(名字跟年齡)
public Student(string name, int age)
{
Name = name; // 指定 field 的值
Age = age;
}
}
現在可以這樣生一個學生:
Student s = new Student("Иван", 21);
Console.WriteLine($"{s.Name}: {s.Age} 歲"); // Иван: 21 歲
- 我們宣告的建構子 visibility(public)跟 class 一樣。
- 建構子裡用傳進來的參數指定 field。
- 這樣可以防止忘東忘西:編譯器會要求你 new 的時候一定要給名字跟年齡。
2. 建構子的參數
C# 可以宣告多個建構子,參數組合不一樣。這叫做建構子多載(method overload 之後還會講)。
比如加一個沒參數的建構子:
public class Student
{
public string Name;
public int Age;
// 預設建構子
public Student()
{
Name = "無名氏";
Age = 0;
}
// 有參數的建構子
public Student(string name, int age)
{
Name = name;
Age = age;
}
}
現在兩種寫法都可以:
Student s1 = new Student(); // 名字:無名氏,年齡:0
Student s2 = new Student("Оля", 22); // 名字:Оля,年齡:22
用建構子做驗證
建構子超適合拿來檢查「輸入資料合不合理」。比如學生年齡不能是負的!
public Student(string name, int age)
{
if (age < 0)
throw new ArgumentException("年齡不能是負的!");
Name = name;
Age = age;
}
現在如果有人想 new 一個 Student("Малыш", -2),程式就會丟出例外(ArgumentException)。這種寫法很常見,讓資料超級可靠。
3. 複雜物件的初始化
實際應用裡,一個物件常常不只 primitive,也會包含其他物件:
public class Group
{
public string GroupName;
public Student[] Students;
public Group(string groupName, Student[] students)
{
GroupName = groupName;
Students = students;
}
}
現在可以很簡單地生一個有學生陣列的 group:
var students = new Student[]
{
new Student("Иван", 20),
new Student("Мария", 19)
};
Group g = new Group("Группа-101", students);
Field 預設值初始化
有時候你會想給某些 field 預設值,如果建構子沒收到那些參數。比如 email 不是必填:
public Student(string name, int age)
{
Name = name;
Age = age;
Email = "unknown@noemail.com"; // 預設值
}
4. 用 Object Initializer 初始化
建構子可以跟另一種很方便的語法搭配 — 物件初始化器。你可以呼叫建構子,然後在大括號裡直接指定新物件的 property 值:
public class Student
{
public string Name { get; set; }
public int Age { get; set; }
public string Email { get; set; }
// 建議還是要有建構子
public Student(string name, int age)
{
Name = name;
Age = age;
Email = "";
}
}
Student s = new Student("Антон", 18) { Email = "anton@domain.com" }; // 建構子 + property
這種寫法很適合有些參數有預設值,或 new 的時候不一定知道全部資料的情境。
5. 常見錯誤與小細節
新手用建構子時常常會遇到幾個經典錯誤。來用手指頭數給你看:
錯誤 #1:名字搞混
有時候參數名跟 field 名只差大小寫,很容易搞混。
public Student(string Name, int Age) // 大寫!
{
Name = Name; // 哎呀!兩個 Name 都指參數,field 沒被初始化。
}
正確寫法: 用 this 指 field:
public Student(string name, int age)
{
this.Name = name;
this.Age = age;
}
或是直接用不同名字(像 nameParam、ageParam),但 this. 這種寫法在大專案裡很推。
錯誤 #2:沒有預設建構子
只要你自己寫了一個建構子,然後又想 new 沒參數的物件 — 會編譯錯誤:
public class Student
{
public Student(string name) { /*...*/ }
}
// 某處 code
Student s = new Student(); // 錯誤!沒有無參數建構子。
解法: 明確加一個沒參數的建構子:
public Student() { /*...*/ }
錯誤 #3:無限遞迴 — 建構子自己呼叫自己
public Student()
{
// 錯誤!自己呼叫自己 — 無限遞迴。
new Student();
}
GO TO FULL VERSION