1. Giới thiệu
Giả sử bạn đang lưu dữ liệu về mấy con mèo trong code. Ngoài tuổi của mèo, bạn còn muốn lưu trạng thái kiểu như "không có dữ liệu", "không biết tuổi". Có thể lưu 0, nhưng số 0 là tuổi hợp lệ mà.
int age = 0; // Tuổi của mèo. Nhưng 0 là gì? Là mèo con hay "không biết"?
Vấn đề là int chỉ nhận giá trị nguyên, còn "không có dữ liệu" thì không có trong số nguyên. Nếu tuổi là string thì có thể gán null. Nhưng với số thì không chơi được trò đó:
int age = null; // Lỗi! Không thể gán null cho biến kiểu int
Lý do là kiểu giá trị (struct như int, double, DateTime, bool) luôn phải chứa gì đó. Không có trạng thái "rỗng" (khác với kiểu tham chiếu, nơi null chỉ là không có object).
Ví dụ thực tế:
Để lách luật này, dev hay nghĩ ra giá trị "đặc biệt": ví dụ -1 hoặc int.MaxValue cho "không có giá trị". Nhưng kiểu này vừa xấu, vừa bất tiện, lại nguy hiểm: dễ nhầm lẫn giá trị thật với giá trị placeholder.
2. Nullable-type: kiểu có thể là "null"
Ý tưởng
Nếu cho phép số bình thường (và tất cả kiểu giá trị) lưu không chỉ giá trị mà còn null thì sao?
Trong C#, điều này được làm bằng class bọc đặc biệt Nullable<T>. Class này có thể chứa giá trị kiểu T hoặc không có gì (tức là null).
Cú pháp
int? age = null; // Biến age có thể là số hoặc null
Khi gặp code kiểu này, compiler C# thực ra hiểu như sau:
Nullable<int> age = new Nullable<int>(); // Biến age không có giá trị
Hoặc nếu muốn gán giá trị rõ ràng:
Nullable<int> age = new Nullable<int>(42); // age chứa số 42
Vậy là dấu hỏi biến bất kỳ kiểu giá trị nào thành phiên bản nullable của nó:
- int? (giống Nullable<int>)
- double?
- DateTime?
- và bất kỳ struct nào khác
Nullable-wrapper cho các struct phổ biến nhất
| Kiểu thường | Nullable-type | Ví dụ ghi |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Có thể ghi dài hơn, dùng class wrapper:
Nullable<int> pages = null;
Nhưng viết int? thì ngắn hơn, nhìn chất hơn.
3. Cách dùng nullable-type
Gán giá trị và kiểm tra
Gán null giống hệt như với kiểu tham chiếu:
Khai báo và gán giá trị
int? temperature = null;
temperature = 25;
Kiểm tra null
Làm sao biết nullable-type của mình có giá trị thật không?
if (temperature != null)
{
Console.WriteLine($"Nhiệt độ: {temperature}");
}
else
{
Console.WriteLine("Không có dữ liệu về nhiệt độ");
}
Thuộc tính của Nullable-type: .HasValue và .Value
Nullable-type có hai thuộc tính quan trọng:
- .HasValue — trả về true nếu biến có gì đó (không phải null).
- .Value — chính là giá trị, nếu có (không thì sẽ bị exception).
int? temperature = null;
if (temperature.HasValue) //không bị exception!
{
Console.WriteLine($"Nhiệt độ: {temperature.Value}°C");
}
else
{
Console.WriteLine("Không biết nhiệt độ");
}
Đoạn code này chạy được: temperature không phải null, nó chứa null bên trong. Bạn luôn có thể gọi thuộc tính HasValue của kiểu Nullable. Nhưng mà lấy .Value khi không có giá trị thì dở lắm, sẽ bị exception InvalidOperationException. Nhớ kiểm tra trước khi lấy giá trị nha!
Cú pháp rút gọn khi in ra
Nhiều khi chỉ cần dùng biến nullable-type là C# tự hiểu hết:
int? hours = null;
Console.WriteLine(hours); // không in gì (trống trơn)
hours = 10;
Console.WriteLine(hours); // in ra 10
4. Toán tử và nullable-type: toán học, so sánh, chuyển đổi
Toán tử: chuyện gì xảy ra?
Nếu một toán hạng là nullable, kết quả cũng nullable.
Nếu một toán hạng là null, kết quả cũng null.
int? a = 5;
int? b = null;
int? sum = a + b; // sum == null
int? c = 10;
int? d = 15;
int? total = c + d; // total == 25
So sánh
Có thể so sánh nullable-type với số bình thường:
int? score = null;
if (score > 0) Console.WriteLine("Kết quả tuyệt vời!");
else Console.WriteLine("Không có kết quả"); // Nhánh này chạy
Nếu score là null, điều kiện score > 0 sẽ là false.
Chuyển đổi tường minh và ngầm định
- Từ kiểu thường sang nullable: tự động.
- Từ nullable sang thường: chỉ khi không phải null (không thì exception).
int? x = 3;
int y = x.Value; // Ok, nếu x không phải null
// Ngầm định
int? z = y;
5. Nullable và method: tham số và giá trị trả về
Trả về nullable-value
Nếu method không phải lúc nào cũng trả về kết quả, hãy dùng nullable-type làm kiểu trả về:
// Method tìm user theo login và trả về tuổi nếu tìm thấy, không thì null
int? FindUserAge(string login)
{
// ... logic tìm kiếm ở đây
return null; // nếu không tìm thấy
}
Nullable-parameter
Có thể nhận nullable-value làm tham số, để rõ ràng là: “có thể có, có thể không”.
void PrintTemperature(int? temp)
{
if (temp == null)
Console.WriteLine("Không biết nhiệt độ");
else
Console.WriteLine($"Nhiệt độ: {temp} độ");
}
GO TO FULL VERSION