Bài 16: Struct, Enum và Record (Đặc sản của C# hiện đại)
🎯 Mục tiêu bài học
-
Dùng Enum để quản lý các trạng thái cố định thay vì dùng số hoặc chuỗi cứng (Hardcode).
-
Phân biệt Struct (Kiểu tham trị) và Class (Kiểu tham chiếu).
-
Nắm bắt Record - Tính năng sinh ra dành riêng cho Backend Developer để quản lý Data Transfer Object (DTO).
📖 Phần 1: Enum (Kiểu liệt kê)
Khi lập trình, bạn thường gặp các trạng thái cố định. Ví dụ: Đơn hàng có các trạng thái 0: Chờ duyệt, 1: Đang giao, 2: Hoàn thành. Nếu bạn dùng số nguyên int status = 1;, code sẽ rất khó đọc vì vài tháng sau bạn không nhớ 1 nghĩa là gì. Enum giúp bạn đặt tên cho các con số đó.
Khai báo và sử dụng Enum
C#
// Khai báo Enum (thường để bên ngoài hoặc ngang hàng với Class)
enum OrderStatus
{
Pending = 0, // Chờ duyệt
Shipping = 1, // Đang giao
Completed = 2, // Hoàn thành
Canceled = 3 // Hủy bỏ
}
class Order
{
public int OrderId { get; set; }
public OrderStatus Status { get; set; } // Dùng Enum làm kiểu dữ liệu
}
// Cách sử dụng trong hàm Main:
Order myOrder = new Order();
myOrder.OrderId = 1001;
myOrder.Status = OrderStatus.Shipping;
// Code cực kỳ tường minh và dễ đọc
if (myOrder.Status == OrderStatus.Shipping)
{
Console.WriteLine("Don hang dang duoc giao den ban.");
}
📖 Phần 2: Struct (Cấu trúc) - "Người em" của Class
Về cú pháp, Struct trông giống hệt Class (cũng có thuộc tính, hàm, constructor). Điểm khác biệt lớn nhất nằm ở Cách lưu trữ trong RAM:
-
Class (Kiểu tham chiếu - Reference Type): Lưu trên vùng nhớ Heap (rộng lớn, linh hoạt nhưng dọn dẹp chậm). Thích hợp cho các đối tượng phức tạp (User, Product, Order).
-
Struct (Kiểu tham trị - Value Type): Lưu trên vùng nhớ Stack (nhỏ gọn, truy xuất siêu tốc, tự động hủy ngay khi dùng xong). Thích hợp cho các cấu trúc dữ liệu rất nhỏ, chỉ chứa vài con số.
C#
// Dùng Struct cho tọa độ 2D vì nó rất nhỏ và cần xử lý nhanh
struct Point2D
{
public int X { get; set; }
public int Y { get; set; }
public Point2D(int x, int y)
{
X = x;
Y = y;
}
}
// Cách dùng giống hệt Class
Point2D p = new Point2D(10, 20);
Nguyên tắc: 95% thời gian làm Backend bạn sẽ dùng
ClasshoặcRecord. Chỉ dùngStructkhi đối tượng cực nhỏ (dưới 16 bytes) và cần tối ưu hiệu năng tính toán khắt khe (như lập trình Game Unity).
📖 Phần 3: Record - Chân ái của Backend Developer
Từ C# 9.0, Microsoft giới thiệu Record. Đây là tính năng sinh ra để làm DTO (Data Transfer Object) - những cục dữ liệu chuyên dùng để giao tiếp giữa API và Database hoặc Client.
Hai sức mạnh của Record:
-
Cú pháp siêu ngắn (Positional Syntax): Khai báo cả một đối tượng có thuộc tính và constructor chỉ trên 1 dòng duy nhất.
-
Tính bất biến (Immutable): Dữ liệu một khi tạo ra sẽ không bị thay đổi ngầm, rất an toàn khi xử lý đa luồng (Multi-threading).
-
So sánh theo giá trị: Hai đối tượng Record khác nhau nhưng chứa dữ liệu giống nhau sẽ được coi là Bằng nhau (Class không làm được điều này nếu không viết thêm code).
C#
// 1 DÒNG DUY NHẤT để tạo ra một cấu trúc có tên, tuổi, vị trí, kèm luôn Constructor và Properties
public record DeveloperDto(string Name, string Role, int ExpYears);
// Cách sử dụng
DeveloperDto dev1 = new DeveloperDto("Nguyen Minh Hieu", "Backend C#", 2);
DeveloperDto dev2 = new DeveloperDto("Nguyen Minh Hieu", "Backend C#", 2);
// Nếu dùng Class, kq = false (vì 2 vùng nhớ khác nhau).
// Nhưng dùng Record, kq = true (vì Record so sánh GIÁ TRỊ bên trong).
Console.WriteLine(dev1 == dev2); // True
// Không thể sửa đổi trực tiếp dữ liệu (vì tính bất biến)
// dev1.Name = "An"; // BÁO LỖI!
💻 Phần 4: Thực hành
Kết hợp Enum và Record để mô phỏng một API trả về dữ liệu người dùng (User Response):
C#
using System;
namespace Lesson16
{
// 1. Enum phân quyền
enum Role
{
Admin,
Editor,
Viewer
}
// 2. Record làm DTO (Data Transfer Object) siêu gọn nhẹ
record UserResponse(int Id, string Username, Role UserRole);
class Program
{
static void Main(string[] args)
{
// Mô phỏng lấy dữ liệu từ Database và gói vào Record
UserResponse response = new UserResponse(1, "hieubackend", Role.Admin);
Console.WriteLine("--- KET QUA TRA VE TU API ---");
Console.WriteLine($"ID: {response.Id}");
Console.WriteLine($"Tai khoan: {response.Username}");
// Xử lý logic bằng Switch Expression cực gọn với Enum
string roleDescription = response.UserRole switch
{
Role.Admin => "Quan tri vien toan quyen",
Role.Editor => "Nguoi bien tap noi dung",
Role.Viewer => "Khach tham quan",
_ => "Khong xac dinh"
};
Console.WriteLine($"Quyen han: {roleDescription}");
Console.ReadLine();
}
}
}
🛠 Bài tập thực hành
Yêu cầu:
-
Tạo một
enum PaymentMethod(Phương thức thanh toán) gồm các giá trị:Cash(Tiền mặt),CreditCard(Thẻ tín dụng),EWallet(Ví điện tử). -
Tạo một
record InvoiceDto(Biên lai) trên 1 dòng code, chứa 3 thông tin: Mã hóa đơn (string), Tổng tiền (double), và Phương thức thanh toán (PaymentMethod). -
Khởi tạo một hóa đơn mới và in thông tin ra màn hình.
All rights reserved