CodeGym /Các khóa học /C# SELF /Nạp chồng constructor

Nạp chồng constructor

C# SELF
Mức độ, Bài học
Có sẵn

1. Giới thiệu

Giả sử tụi mình có class Person:

public class Person
{
    public string Name;
    public int Age;
    
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

Và bạn cảm thấy mình gần như thiên tài — có thể tạo người với bất kỳ tên và tuổi nào. Nhưng mà, đôi khi thông tin lại không đủ! User app chỉ nhập tên thôi, còn tuổi thì biết sau. Hoặc bạn muốn set "tuổi mặc định" trong vài trường hợp. Dĩ nhiên có thể nghĩ ra cách lách, nhưng C# cho bạn giải pháp cực kỳ gọn — nạp chồng constructor.

Nạp chồng constructor nghĩa là một class có thể có nhiều constructor cùng tên (tên class), nhưng khác nhau về danh sách tham số.

Danh sách này khác nhau thế nào? Có thể khác về:

  1. Số lượng tham số: Ví dụ, một constructor nhận 2 tham số, cái khác — 3.
  2. Kiểu tham số: Một cái nhận (string, int), cái khác — (string, decimal).
  3. Thứ tự tham số: (string, int) và (int, string) — là hai signature khác nhau.

Điều quan trọng nhất: compiler phân biệt constructor (và cả method) theo signature. Signature gồm tên constructor (hoặc method) và danh sách tham số (số lượng, kiểu và thứ tự). Access modifier (public, private) và, với method thường, kiểu trả về, không phải là phần của signature. Nhưng constructor thì không có kiểu trả về, và tên luôn trùng tên class.

2. Nạp chồng constructor: cú pháp và ví dụ


public class Cat
{
    public string Name;
    public string Color;

    // Constructor không tham số
    public Cat()
    {
        Name = "Mèo không tên";
        Color = "Xám";
    }

    // Constructor với một tham số
    public Cat(string name)
    {
        Name = name;
        Color = "Xám";
    }

    // Constructor với hai tham số
    public Cat(string name, string color)
    {
        Name = name;
        Color = color;
    }
}

Cách dùng:

Cat barsik = new Cat(); // "Mèo không tên", "Xám"
Cat murzik = new Cat("Murzik"); // "Murzik", "Xám"
Cat ryzhik = new Cat("Ryzhik", "Cam"); // "Ryzhik", "Cam"

Khi tạo object mà bạn truyền tham số, C# sẽ "đoán" constructor đúng dựa vào số lượng và kiểu tham số.

Rất tiện luôn! User class của bạn không cần truyền tham số không biết: chỉ muốn mèo thôi — dùng constructor không tham số, biết tên — dùng cái khác, v.v. Muốn tự chọn màu — lấy option thứ ba.

3. Cách gọi constructor nạp chồng hoạt động thế nào?

C# chọn constructor phù hợp tự động lúc compile, dựa vào kiểu và số lượng tham số truyền vào. Khó mà nhầm — nếu có gì sai, compiler sẽ báo lỗi ngay.

Thử thêm constructor với tham số kiểu int:

public Cat(int age)
{
    Name = "Mèo không tên";
    Color = "Xám";
    // Code thêm cho tuổi
}

Giờ bạn có thể tạo "mèo theo tuổi":

Cat oldCat = new Cat(5); // Sẽ gọi constructor Cat(int age)
Gọi constructor Sẽ gọi cái nào?
new Cat()
Không tham số
new Cat("Murka")
Một tham số kiểu string
new Cat("Ryzhik", "Cam")
Hai tham số kiểu string
new Cat(10)
Một tham số kiểu int

4. Gọi constructor bên trong: từ khóa this

Đôi khi các constructor khác nhau lại làm việc giống nhau. Để khỏi copy code, bạn có thể "gọi constructor này từ constructor khác". Dùng từ khóa this nhé.


public class Cat
{
    public string Name;
    public string Color;

    public Cat() : this("Mèo không tên")
    {
        // Constructor này gọi Cat(string name)
    }

    public Cat(string name) : this(name, "Xám")
    {
        // Constructor này gọi Cat(string name, string color)
    }

    public Cat(string name, string color)
    {
        Name = name;
        Color = color;
    }
}

Kết quả:

  • new Cat() gọi Cat(string name), cái này lại gọi Cat(string name, string color).
  • Tất cả "đường đều về La Mã": tức là mọi khởi tạo đều tập trung ở một constructor "chính".

Cách này gọi là constructor chaining (chuỗi gọi constructor).

5. Ví dụ thực tế

Làm thử class hero (Hero) cho game, có tên và level! Viết như này nè:

Một constructor:

public class Hero
{
    public string Name;
    public int Level;

    public Hero(string name, int level)
    {
        Name = name;
        Level = level;
    }
}

Giờ cho phép tạo hero nhiều kiểu khác nhau!

Nhiều constructor:

public class Hero
{
    public string Name;
    public int Level;

    // Nếu không truyền gì, hero sẽ là "Không tên level 1"
    public Hero() : this("Không tên", 1)
    {
    }

    // Nếu chỉ biết tên, level mặc định — 1
    public Hero(string name) : this(name, 1)
    {
    }

    // Constructor chính với hai tham số
    public Hero(string name, int level)
    {
        Name = name;
        Level = level;
    }
}

Giờ có thể tạo hero theo nhiều cách:

Hero h1 = new Hero();               // "Không tên", 1
Hero h2 = new Hero("Arthur");        // "Arthur", 1
Hero h3 = new Hero("Laura", 10);     // "Laura", 10

6. Chi tiết thực thi: bẫy ẩn và lưu ý

Thứ nhất: compiler sẽ không tự tạo constructor "rỗng" mặc định nếu bạn đã tự khai báo bất kỳ constructor nào khác. Nên nếu bạn viết constructor có tham số mà quên thêm constructor không tham số, thì gọi như này:

Hero h = new Hero(); // Lỗi, nếu không có constructor không tham số!
sẽ báo lỗi compile.

Thứ hai: nếu trong chuỗi gọi constructor mà gọi nhầm, có thể làm sai logic khởi tạo. Nên giữ nhất quán: tốt nhất mọi xử lý thực sự đều ở constructor đầy đủ nhất, còn các constructor khác chỉ truyền dữ liệu qua this(...).

Thứ ba: nếu tham số chỉ khác kiểu (ví dụ, Cat(string s) và Cat(object o)), có thể bị nhầm khi truyền tham số kiểu null. Compiler không phải lúc nào cũng biết bạn muốn gọi constructor nào.

7. Nạp chồng và khởi tạo field

Thường trong class có field bắt buộc phải set. Nạp chồng constructor giúp bạn làm việc này tiện hơn, nhưng bạn tự quyết định giá trị mặc định nào hợp lý cho class của mình.

Ví dụ có kiểm tra:

public class Book
{
    public string Title;
    public int Year;

    // Sách mặc định — "Không tên", năm 2000
    public Book() : this("Không tên", 2000)
    {
    }
    
    public Book(string title) : this(title, 2000)
    {
    }

    public Book(string title, int year)
    {
        Title = title;
        Year = year;
    }
}

8. Nạp chồng với nhiều bộ tham số khác nhau

Có những class có cả chục constructor! Không phải lúc nào cũng là kiến trúc tốt (đôi khi nên dùng method "cấu hình" hoặc pattern "builder"), nhưng với object cho user, model, DTO — vẫn ổn áp.

Ví dụ: class "Thú cưng", có thể truyền đủ mọi thuộc tính, hoặc chỉ những cái cần thiết.

public class Pet
{
    public string Name;
    public int Age;
    public string Type;
    public bool IsVaccinated;

    // Constructor mặc định
    public Pet() : this("NoName", 0, "Cat", false) { }

    // Thông tin tối thiểu
    public Pet(string name, string type) : this(name, 0, type, false) { }

    // Constructor đầy đủ
    public Pet(string name, int age, string type, bool isVaccinated)
    {
        Name = name;
        Age = age;
        Type = type;
        IsVaccinated = isVaccinated;
    }
}
Sự khác biệt giữa nạp chồng method và constructor
Method thường Constructor (kể cả nạp chồng)
Có thể gọi bất cứ lúc nào Chỉ gọi khi tạo object (new)
Có thể trả về giá trị bất kỳ Không bao giờ trả về giá trị (không có kiểu trả về)
Tên method tùy ý, thường là động từ Tên luôn trùng tên class
Có thể nạp chồng theo tham số Cũng nạp chồng theo tham số
2
Nhiệm vụ
C# SELF, mức độ, bài học
Đã khóa
Tạo lớp với overload constructor
Tạo lớp với overload constructor
2
Nhiệm vụ
C# SELF, mức độ, bài học
Đã khóa
Gọi constructor thông qua `this`
Gọi constructor thông qua `this`
1
Khảo sát/đố vui
, cấp độ , bài học
Không có sẵn
Khái niệm về lớp và đối tượng
Lớp và constructor
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION