CodeGym /Các khóa học /C# SELF /Những lỗi thường gặp khi khai báo class và object

Những lỗi thường gặp khi khai báo class và object

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

1. Vấn đề trùng tên: field, property, parameter

Người mới thường gặp trường hợp tên property, parameter của constructor và field private bị trùng nhau. Lúc này compiler không báo lỗi, nhưng kết quả có thể không như bạn mong đợi đâu.

Ví dụ

public class Student
{
    private string name;

    public Student(string name)
    {
        name = name; // "Tưởng đúng mà lại sai..."
    }

    public void PrintName()
    {
        Console.WriteLine(name);
    }
}

Chuyện gì sẽ xảy ra? Method PrintName() sẽ không có gì để in ra: field private name vẫn sẽ là null! Bởi vì trong constructor, name = name; chỉ làm việc với parameter local, không phải field của class.

Làm sao tránh

Dùng từ khóa this để truy cập field/property của class:

public Student(string name)
{
    this.name = name; // Giờ thì ổn rồi!
}

Lưu ý: Rider và các IDE khác sẽ giúp bạn phát hiện lỗi này — đừng bỏ qua gợi ý của chúng nhé!

2. Thiếu constructor mặc định: khi nào cần?

Nếu bạn đã khai báo ít nhất một constructor riêng, compiler sẽ không tự tạo constructor "rỗng" mặc định nữa. Điều này hay gây lỗi, nhất là khi bạn muốn tạo object không truyền parameter.

Ví dụ

public class Book
{
    public string Title { get; set; }

    // Constructor khai báo rõ ràng
    public Book(string title)
    {
        Title = title;
    }
}

var book = new Book(); // Compiler: "Bíp-bíp! Không có constructor không parameter!"

Nếu bạn muốn tạo instance cả không có parameter lẫn có parameter, nhớ khai báo constructor cần thiết:

public Book() { }

public Book(string title)
{
    Title = title;
}

3. Modifier truy cập: đời tư của người nổi tiếng

Lỗi phổ biến của newbie là không nghĩ đến modifier truy cập. Mặc định, field và method của class có modifier private, còn member của struct cũng là private. Nếu không khai báo rõ hoặc quên, bạn sẽ không thể dùng property hay method đó bên ngoài class.

Ví dụ

public class Animal
{
    string name; // private mặc định

    public void PrintName()
    {
        Console.WriteLine(name);
    }
}

var animal = new Animal();
animal.name = "Barsik"; // Lỗi compile!

Compiler sẽ không cho phép truy cập member private. Đúng hơn là nên khai báo modifier rõ ràng:

public string Name;

hoặc

private string name;

Để truy cập hợp lý hơn, hãy dùng property:

public string Name { get; set; }

4. Quên khởi tạo field: bẫy null

Bẫy rất phổ biến — field của class chưa được khởi tạo. Đặc biệt nguy hiểm nếu field đó sẽ được dùng trong method gọi trước khi khởi tạo rõ ràng.

Ví dụ

public class Cat
{
    public string Name;

    public void SayHello()
    {
        Console.WriteLine($"Meo! Độ dài tên là {Name.Length} ký tự!"); // Có thể bị NullReferenceException
    }
}

var cat = new Cat();
cat.SayHello(); // Thảm họa!

Biến Name bằng null, và khi truy cập Length sẽ bị NullReferenceException. Hãy chú ý trạng thái object sau khi tạo!

Mẹo: Dùng constructor để bắt buộc truyền giá trị cho field quan trọng, còn property thì đánh dấu là bắt buộc (trong C# mới — dùng required).

5. Đệ quy vô tận trong auto-property: lỗi copy-paste

Có lúc bạn viết property thủ công và nhầm lẫn — ví dụ, trong getter hoặc setter lại truy cập chính property đó (không phải field private), dẫn đến lặp vô tận.

Ví dụ

public class Dog
{
    public int Age
    {
        get { return Age; } // Ôi!
        set { Age = value; } // Ôi trời!
    }
}

Code này mỗi lần gọi Age sẽ tự gọi lại chính nó... vô tận (cho đến khi stack tràn — tự ăn chính mình).

Làm đúng

Dùng field private riêng:

private int age;

public int Age
{
    get { return age; }
    set { age = value; }
}

Hoặc dùng auto-property:

public int Age { get; set; }

6. Không nhất quán tên biến

Lập trình cần cẩn thận với chữ hoa/thường và tên biến. C# là ngôn ngữ phân biệt chữ hoa/thường (Case-Sensitive), nên Name, name, NAME là ba identifier khác nhau.

Ngoài ra, nếu bạn đổi tên field hay property, nhớ sửa ở mọi chỗ! Rider sẽ giúp refactor, nhưng không phải lúc nào cũng tránh được lỗi gõ nhầm (nhất là khi tên giống nhau).

7. Dùng sai modifier (static/instance)

Đôi khi newbie nhầm lẫn giữa method instance và static. Vấn đề xảy ra khi bạn muốn truy cập field/method không đúng kiểu như syntax của C# cho phép.

Ví dụ

public class MathHelper
{
    public static int Add(int a, int b) => a + b;
    public int Multiply(int a, int b) => a * b;
}

var sum = MathHelper.Add(2, 3); // OK
var mul = MathHelper.Multiply(2, 3); // Lỗi! 

Method Multiply không phải static, không thể gọi qua tên class mà chưa tạo object:

var helper = new MathHelper();
var mul = helper.Multiply(2, 3); // Giờ thì đúng rồi

8. Lỗi khi kế thừa constructor

Nếu class của bạn kế thừa từ class khác, nhớ rằng class cha có thể yêu cầu truyền parameter vào constructor của nó.

Ví dụ

public class Animal
{
    public string Name;

    public Animal(string name)
    {
        Name = name;
    }
}

public class Dog : Animal
{
    public Dog() { } // Lỗi: class cha yêu cầu name!
}

Compiler sẽ không cho bạn khai báo constructor như vậy. Cần gọi rõ constructor của class cha:

public class Dog : Animal
{
    public Dog(string name) : base(name) { }
}

9. Quên implement đủ member của interface

Nếu class của bạn implement interface mà chưa implement đủ method của nó — sẽ bị lỗi compile: "Class chưa implement đầy đủ interface".

Ví dụ

public interface IWalker
{
    void Walk();
    void Run();
}

public class Turtle : IWalker
{
    public void Walk()
    {
        Console.WriteLine("Chậm mà chắc...");
    }
    // Ôi! Quên Run rồi!
}

IDE sẽ báo lỗi, và Turtle sẽ không compile được cho đến khi bạn implement đủ method của interface.

10. Dùng object chưa khởi tạo

Trong C#, object được tạo rõ ràng bằng new. Nếu bạn chỉ khai báo biến tham chiếu mà chưa gán object — nó sẽ là null. Khi truy cập field/method sẽ bị NullReferenceException nổi tiếng.

Ví dụ

Student student;
student.PrintName(); // Bất ngờ chưa!

Nhất định phải khởi tạo object:

Student student = new Student("Vasilisa");
student.PrintName();

11. Lỗi: field public thay vì property

"Anti-pattern" cũ: khai báo tất cả field của class là public. Tại sao lại dở? Field không được bảo vệ, không kiểm soát thay đổi, phá vỡ encapsulation!

Ví dụ

public class Car
{
    public int speed; // Đừng làm vậy!
}

Tốt hơn là dùng property:

public class Car
{
    public int Speed { get; set; }
}

Property cho phép bạn thêm kiểm tra, logic, giới hạn:

private int speed;
public int Speed
{
    get { return speed; }
    set
    {
        if (value < 0) speed = 0;
        else speed = value;
    }
}

12. Tạo "magic number" hoặc string không rõ ràng

Nếu bạn thấy trong code có 42, "Ivan", "SomeFile.txt" và các giá trị "ma thuật" khác ngay trong method — đó là lỗi tiềm ẩn. Tốt hơn nên dùng constant hoặc field.

Ví dụ

public class ConfigManager
{
    public void Save()
    {
        File.WriteAllText("config.txt", "some data"); // String ma thuật!
    }
}

Tốt hơn:

public class ConfigManager
{
    private const string ConfigFileName = "config.txt";
    public void Save()
    {
        File.WriteAllText(ConfigFileName, "some data");
    }
}

13. Lỗi copy: biến tham chiếu cùng một object

Trong C#, biến object là tham chiếu. Gán biến này cho biến khác chỉ copy tham chiếu, không phải object thật. Điều này hay gây ra thay đổi "bất ngờ".

Ví dụ

Student s1 = new Student("Vasya");
Student s2 = s1;
s1.Name = "Kolya";
Console.WriteLine(s2.Name); // Sẽ in ra "Kolya"!

Tại sao? Vì s1s2 đều trỏ đến cùng một object!

14. Thiếu phản hồi khi có lỗi

Dở khi class cho phép gán giá trị không hợp lệ. Ví dụ, tuổi âm.

Ví dụ

public class Human
{
    public int Age { get; set; }
}

var h = new Human();
h.Age = -50; // Không vấn đề gì...

Logic của class nên bảo vệ khỏi giá trị vô lý:

private int age;
public int Age
{
    get { return age; }
    set
    {
        if (value < 0)
            throw new ArgumentException("Tuổi không thể âm.");
        age = value;
    }
}

15. Nhầm lẫn phạm vi biến (scope)

Đôi khi biến được khai báo trùng tên ở các phạm vi khác nhau — trong method và field của class.

Ví dụ

public class MyClass
{
    int value = 10;

    public void Foo()
    {
        int value = 99;
        Console.WriteLine(value); // Sẽ in ra 99, không phải 10!
    }
}

Để truy cập rõ ràng field của class, dùng this.value.

16. Override method mà quên từ khóa override

Khi bạn muốn override method virtual của class cha, đừng quên override!

Ví dụ

public class Animal
{
    public virtual void Speak() => Console.WriteLine("Động vật nói");
}

public class Cat : Animal
{
    public void Speak() => Console.WriteLine("Meo!"); // Không phải override!
}

Trong trường hợp này, method của class cha không bị override: method mới chỉ che khuất method cũ (shadowing), có thể gây bất ngờ khi làm việc với reference của class cha.

Đúng là:

public override void Speak() => Console.WriteLine("Meo!");

17. Gọi method virtual trong constructor

Đây không hẳn là lỗi compile, nhưng hay gây bug khó phát hiện. Khi constructor của class cha gọi method virtual, mà method đó đã bị override ở class con — lúc gọi có thể field của class con chưa được khởi tạo.

Ví dụ

public class Animal
{
    public Animal()
    {
        Speak();
    }

    public virtual void Speak()
    {
        Console.WriteLine("Động vật...");
    }
}

public class Cat : Animal
{
    public string Name { get; set; } = "Barsik";

    public override void Speak()
    {
        Console.WriteLine($"Meo! Tôi là {Name}");
    }
}

Cat cat = new Cat();
// Khi gọi Speak() constructor của Cat CHƯA chạy xong, Name vẫn null!

18. Chỉ mô tả object qua constructor (không có property)

Nếu class chỉ có constructor, sau khi tạo object không thể thay đổi gì — tốt cho object bất biến, nhưng không phải lúc nào cũng tiện. Thường thì nên cho phép user của class thay đổi property (hoặc ít nhất một số property).

19. Bỏ qua quy ước đặt tên chuẩn (naming conventions)

Tên class bắt đầu bằng chữ hoa, method cũng vậy, field thì chữ thường và gạch dưới (hoặc camelCase), constant — TOÀN CHỮ HOA. Dùng style thống nhất không chỉ giúp code dễ đọc mà còn giảm nguy cơ lỗi bất ngờ!

20. Field và method không dùng đến

Nếu bạn tạo field mà không dùng ở đâu — đó là "dead code". IDE sẽ highlight cho bạn. Càng ít "vùng chết" thì càng dễ bảo trì app!

2
Nhiệm vụ
C# SELF, mức độ, bài học
Đã khóa
Sửa lỗi trùng tên và sử dụng từ khóa `this`
Sửa lỗi trùng tên và sử dụng từ khóa `this`
2
Nhiệm vụ
C# SELF, mức độ, bài học
Đã khóa
Thêm constructor mặc định
Thêm constructor mặc định
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION