CodeGym /Các khóa học /C# SELF /Giới thiệu về Nullable Reference Types

Giới thiệu về Nullable Reference Types

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

1. Vấn đề NullReferenceException

Gần như ai mới học (và cả không mới) lập trình C# đều từng gặp thông báo đáng sợ này:


System.NullReferenceException: Tham chiếu đối tượng chưa được gán tới instance của object.

Đây là ví dụ kinh điển:

string hello = null;
Console.WriteLine(hello.Length); // BÙM! NullReferenceException

Ý nghĩa đơn giản: bạn đang cố truy cập một object không tồn tại. Tức là biến đang trỏ tới null — chỗ trống thay vì object.

Tại sao dễ mắc lỗi này?

Bởi vì hầu hết reference types trong C# từ xưa đến nay đều có thể nhận giá trị null. Lập trình viên thường quên kiểm tra biến thực sự trỏ tới object chưa, và thế là dính ngay NullReferenceException nổi tiếng.

Nếu lập trình viên mà được 1 đô cho mỗi lần NullReferenceException, chắc giờ đã tự viết xong hệ điều hành của mình rồi.

Tại sao đây là vấn đề?

Trước đây mình đã học cách để kiểu int? hoặc double? có thể bằng null — cho trường hợp cần thể hiện rõ "không có giá trị" (ví dụ, trường trong database có thể chưa điền).

Nhưng reference types (ví dụ string, bất kỳ class nào, mảng, v.v.) luôn có thể bằng null. Đó là từ khi C# ra đời. Tiện thì tiện, nhưng cũng nguy hiểm — vì ngôn ngữ không bắt mình phải nghĩ: "Liệu tham chiếu này có thể trống không nhỉ?"

2. Tiến hóa: Nullable Reference Types (NRT)

5 năm trước, có một cải tiến đã đảo lộn cách xử lý NullReferenceExceptionNullable Reference Types (NRT).

string s = "hello";   // not-nullable reference
string? maybe = null; // nullable reference
Sự khác biệt giữa not-nullable và nullable reference types

Ý tưởng chính:

  • Phân biệt rõ biến tham chiếu nào chắc chắn không thểnull, và biến nào có thể trống.
  • Giúp lập trình viên nhìn thấy vấn đề tiềm ẩn với null ngay khi compile, chứ không phải lúc chạy (lúc đó thì muộn rồi).

Trong các phiên bản mới của C#, cách khai báo biến tham chiếu đã thay đổi: mặc định không được gán null, trừ khi bạn cho phép rõ ràng.

Chỉ một ký tự ? thôi mà ý nghĩa biến đã khác hẳn!

3. Bật Nullable Reference Types

Mặc định cú pháp nghiêm ngặt này bị tắt trong đa số project, để không làm hỏng code cũ. Nhưng template project mới trong Visual Studio, Rider và .NET CLI đã tạo project với NRT bật sẵn — hoặc ít nhất cũng khuyên nên bật.

Để kiểm tra NRT đã bật chưa, tìm trong file .csproj dòng này:

<Nullable>enable</Nullable>
Bật NRT trong project

Nếu chưa có dòng này, bạn có thể tự thêm vào — an toàn nhé.

Ảnh hưởng tới code như nào?

  • Nếu NRT tắt (kiểu cũ): string s = null; — không lỗi, ai cũng kệ.
  • Nếu bật: compiler sẽ báo lỗi nếu bạn cố gán null cho cái không nên null.

4. Ví dụ với NRT

Ví dụ đơn giản


#nullable enable // Dòng này bật kiểm tra NRT cho file này

string notNullable = "Xin chào";
string notNullable2 = null; // LỖI compile!
string? nullableString = null; // Ok, mình cho phép null rõ ràng
Ví dụ kiểm tra nghiêm ngặt null

Dòng đầu mình khai báo string luôn phải trỏ tới object thật.
Dòng thứ ba — khai báo string có thể là null.

Kiểm tra null

void PrintLength(string? s)
{
    // Compiler sẽ báo: "Nếu s == null thì sao?"
    Console.WriteLine(s.Length); 
    
    // Làm thế này thì ok
    if (s != null)
    {
        Console.WriteLine(s.Length); 
    }
}

Compiler giờ giúp mình không quên kiểm tra nữa!

Cảnh báo của compiler

Nếu bạn lơ cảnh báo và vẫn truy cập biến nullable mà không kiểm tra — sẽ có cảnh báo mới (rất hữu ích!) từ compiler. Không phải lỗi (chương trình vẫn build được), nhưng sẽ có "bóng đèn vàng" nhắc: "Bạn chắc chưa, bro?"

So sánh các chế độ:

Kiểu C# Có thể null? Chế độ Legacy (trước NRT) Chế độ NRT (#nullable enable)
int Không Không Không
int?
string Không (luôn được) Không (mặc định không được)
string? Không

5. Mẹo: dùng NRT thế nào và để làm gì

  • Giảm bug: Ít crash bất ngờ vì null, dev và user đều vui hơn.
  • Code rõ ràng: Nhìn phát biết ngay cái nào có thể trống, cái nào luôn phải có giá trị.
  • Compiler giúp đỡ: Phải công nhận — nó thực sự cố gắng vì mình! Cảnh báo NRT là nguồn thông tin quý về lỗi tiềm ẩn.

Khi nào không thể thiếu

  • Dự án lớn, nhiều người cùng sửa code.
  • API và thư viện public — để giải thích cho người khác biết nên dùng thế nào.
  • Nơi cần độ tin cậy cao (ví dụ app ngân hàng, hệ thống y tế, v.v.)

6. Lỗi thường gặp và bẫy

  • "Quên gắn ?"
    Gán null cho string thường (string s = null;) — compiler báo lỗi, vì mặc định giờ string thường không được null.
  • "Lạm dụng ?"
    Làm tất cả biến thành string? chỉ để compiler không báo. Ý nghĩa là phải đánh dấu cẩn thận chỗ nào thực sự cho phép trống.
  • "Hiểu nhầm cảnh báo"
    Lơ cảnh báo, rồi lại dính NullReferenceException đúng chỗ tưởng compiler sẽ bảo vệ.
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION