C# - C Sharp: Hàm tạo (Constructor)
Tổng quan
Mỗi lớp có thể chứa nhiều trường trong đó việc khai báo và khởi tạo sẽ trở nên khó khăn để theo dõi nếu chúng được thực hiện bên trong các khối khác nhau. Tương tự như vậy, có thể có những hoạt động khởi đầu khác mà cần được giải quyết trong một ứng dụng, chẳng hạn như mở một tập tin. C# cho phép các đối tượng khởi tạo chính bản thân chúng một cách trực tiếp ngay trong quá trình tạo chúng. Điều này được được thực hiện bằng cách định nghĩa các hàm tạo (constructor) trong lớp.
Mỗi hàm tạo là một phương thức có tên giống với tên của lớp chứa nó. Hàm tạo có nhiệm vụ khởi tạo giá trị cho các trường hoặc thực hiện các hoạt động khởi tạo chỉ một lần trong khi tạo một đối tượng của lớp. Các hàm tạo sẽ tự động được gọi và thực thi mỗi khi một đối tượng được tạo và gọi đến hàm tạo đó.
Chú ý: Hàm tạo không có kiểu trả về. Điều này là bởi vì kiểu trả về ngầm định của hàm tạo chính là bản thân lớp chứa nó. Bạn cũng có quyền áp dụng hình thức tải chồng phương thức đối với hàm tạo.
Có hai loại hàm tạo chính như sau:
1. Hàm tạo không tham số
Cú pháp
Cú pháp khai báo hàm tạo không tham số của một lớp là như sau:
//Các câu lệnh khởi tạo
}
Ví dụ dưới đây định nghĩa một lớp có tên là Animal
có một hàm tạo không tham số.
//tạo lớp Animal: public class Animal { //khai báo các trường: public int id; //mã nhận diện public string name; //tên public float age; //tuổi //hàm tạo không tham số public Animal() { //khởi tạo giá trị ban đầu cho các trường id = 1234; name = "Moon"; age = 1; } }
Trong đoạn mã trên có định nghĩa một phương thức có tên Animal()
và đây chính là hàm tạo. Phương thức này sẽ khởi tạo giá trị cho các trường id, name và age khi đối tượng của lớp được tạo và gọi tới nó. Hàm tạo này không có bất kỳ tham số nào, vì thế nó được gọi là hàm tạo không tham số.
Lời gọi tới hàm tạo
Hàm tạo được gọi trực tiếp trong quá trình tạo đối tượng. Có nghĩa là mỗi khi toán tử new xuất hiện thì bộ nhớ sẽ được cấp phát cho đối tượng. Hàm tạo, nếu được định nghĩa trong lớp thì sẽ được dùng để khởi tạo giá trị ban đầu cho các trường của đối tượng. Ví dụ:
class TestAnimal { public static void Main(string[] args) { Animal cat = new Animal(); //gọi tới hàm tạo không tham số Console.WriteLine($"ID: {cat.id}"); Console.WriteLine($"Name: {cat.name}"); Console.WriteLine($"Age: {cat.age}"); } }
Đoạn mã trên tạo một đối tượng có tên cat. Trước tiên đối tượng của lớp Animal sẽ được cấp phát vùng nhớ và sau đó hàm tạo không tham số Animal() được gọi. Vì thế hàm tạo không tham số này (như đã được định nghĩa ở ví dụ phía trên) sẽ khởi tạo giá trị cho các trường của đối tượng cat là id, name và age.
Kết quả:
ID: 1234 Name: Moon Age: 1.0
2. Hàm tạo mặc định
Trong trường hợp bạn không định nghĩa hàm tạo nào trong lớp, thì hệ thống sẽ gọi tới hàm tạo mặc định (được trình dịch cung cấp) để khởi tạo các đối tượng. Hàm tạo mặc định này không có tham số và nó sẽ khởi tạo giá trị cho các trường của đối tượng mới các giá trị mặc định ứng với kiểu dữ liệu của từng trường.
Ví dụ sau định nghĩa lớp có tên Employee nhưng không định nghĩa hàm tạo nào.
namespace ConsoleApp1 { public class Employee { // khai báo các trường string tenNhanVien; int tuoiNhanVien; double luongNhanVien; bool tinhTrangHonNhan; /* phương thức để hiển thị thông tin chi tiết nhân viên */ public void EmployeeDetail() { Console.WriteLine("Chi tiết nhân viên:"); Console.WriteLine("================"); Console.WriteLine("Tên: " + tenNhanVien); Console.WriteLine("Tuổi: " + tuoiNhanVien); Console.WriteLine("Lương: " + luongNhanVien); Console.WriteLine("Tình trạng hôn nhân: " + tinhTrangHonNhan); } } }
Ví dụ trên khai báo một lớp Employee với các trường và một phương thức là EmployeeDetail(). Phương thức này in giá trị của các trường ra màn hình.
Ví dụ sau đây tạo một lớp có chứa phương thức Main() trong đó tạo một đối tượng của lớp Employee ở trên và từ đó gọi phương thức EmployeeDetail() của nó.
public class TestEmployee { public static void Main(string[] args) { Employee objEmp = new Employee(); objEmp.EmployeeDetail(); } }
Vì lớp Employee không có bất kỳ hàm tạo nào được định nghĩa, nên hàm tạo mặc định sẽ được tạo khi thực thi chương trình.
Khi câu lệnh new Employee() được thực thi, thì đối tượng được cấp phát vùng nhớ và các trường được hàm tạo mặc định khởi tạo các giá trị ứng với từng kiểu dữ liệu. Sau đó, phương thức EmployeeDetail() được thực thi để hiển thị các giá trị của các biến thể hiện của đối tượng objEmp.
Bảng dưới đây liệt kê các giá trị mặc định của từng kiểu dữ liệu sẽ được gán cho trường của lớp nếu lớp đó không có định nghĩa hàm tạo.
Kiểu dữ liệu | Giá trị mặc định |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0L |
float | 0.0f |
double | 0.0 |
char | '\u0000' |
bool | false |
string | null |
3. Hàm tạo có tham số
Trong ví dụ bên trên có định nghĩa hàm tạo không tham số cho lớp Animal, trong đó gán giá trị 1234 cho trường id, "Moon" cho trường name, và 1 cho trường age. Và điều này cũng có nghĩa là tất cả các đối tượng của lớp Animal đều sẽ được khởi tạo các giá trị giống nhau cho các trường khi hàm tạo không tham số được gọi. Điều này không hữu dụng trong nhiều tình huống.
Để khắc phục điều này ta có thể định nghĩa hàm tạo chứa một danh sách các tham số với mục đích khởi tạo giá trị cho các trường của đối tượng tương ứng. Các giá trị khởi tạo sẽ được truyền cho hàm tạo tương ứng được gọi trong quá trình tạo đối tượng.
Ví dụ sau đây sẽ định nghĩa thêm hàm tạo ba tham số cho lớp Animal.
//tạo lớp Animal: public class Animal { //khai báo các trường: int id; //mã nhận diện String name; //tên float age; //tuổi //hàm tạo không tham số public Animal() { id=1234; name="Moon"; age=1; } //hàm tạo 3 tham số public Animal(int id, String name, float age) { this.id = id; this.name = name; this.age = age; } }
Ví dụ trên khai báo một hàm tạo ba tham số là Animal(int id, string name, float age). Trong quá trình thực thi, hàm tạo sẽ chấp nhận giá trị của ba tham số và gán chúng cho các trường id, name, và age của đối tượng được tạo.
Dưới đây là một ví dụ hoàn chỉnh, trong đó trong lớp Animal tạo thêm một phương thức có tên ShowInfo() để hiển thị thông tin các trường của đối tượng, trong phương thức Main() của lớp TestAnimal tạo hai đối tượng có tên cat và dog, trong đó đối tượng cat gọi đến phương thức không tham số, còn đối tượng dog gọi đến phương thức ba tham số.
namespace ConsoleApp1 { //tạo lớp Animal: public class Animal { //khai báo các trường: public int id; //mã nhận diện public string name; //tên public float age; //tuổi //hàm tạo không tham số public Animal() { //khởi tạo giá trị ban đầu cho các trường id = 1234; name = "Moon"; age = 1; } //hàm tạo 3 tham số public Animal(int id, string name, float age) { this.id = id; this.name = name; this.age = age; } //phương thức để show thông tin của các trường public void ShowInfo() { Console.WriteLine("ID: " + id); Console.WriteLine("Name: " + name); Console.WriteLine("Age: " + age); } } //tạo lớp TestAnimal: class TestAnimal { static void Main(string[] args) { Animal cat = new Animal(); cat.ShowInfo(); Animal dog = new Animal(5678, "Phu Quoc", 1.5f); dog.ShowInfo(); } } }
Kết quả:
ID: 1234
Name: Moon
Age: 1
ID: 5678
Name: Phu Quoc
Age: 1.5
Trong quá trình tạo đối tượng dog, những điều sau đây xảy ra theo trình tự:
- Việc cấp phát bộ nhớ được thực hiện cho đối tượng mới của lớp (từ khóa new làm điều này).
- Các giá trị 5678, "Phu Quoc", và 1.5f được truyền tới hàm tạo ba tham số là Animal(int id, string name, float age) trong đó khởi tạo giá trị cho các trường của đối tượng là id, name, và age.
- Cuối cùng, tham chiếu của đối tượng mới được tạo được trả về và lưu trong biến tham chiếu dog.