
Smart Enums một giải pháp tuyệt vời khi làm việc với state flow
February 25, 2025Bài viết về workflow mình có nhắc đến kỹ thuật này khi làm state flow rồi, bạn có thể đọc lại tại đây: Cách xây dựng một workflow thông qua masstransit saga state machine
Bài viết này mình sẽ nói chi tiết hơn về Smart Enum và cách implement nó
Bắt đầu thôi!
Smart Enums là một tính năng trong C# giúp mã nguồn trở nên dễ đọc và dễ hiểu hơn. Theo truyền thống, một kiểu enum đơn giản chỉ là một kiểu dữ liệu chứa tập hợp các giá trị hằng số. Tuy nhiên, Smart Enums mở rộng chức năng của enum bằng cách bổ sung các khả năng mới.

Ví dụ, một Smart Enum có thể cung cấp một phương thức Parse để ánh xạ một chuỗi sang một giá trị enum. Điều này cho phép bạn sử dụng hằng số enum dưới dạng chuỗi trong mã nguồn thay vì phải sử dụng hằng số enum cụ thể. Ngoài ra, Smart Enums còn hỗ trợ việc bổ sung các hành vi tùy chỉnh cho từng hằng số enum.
Chẳng hạn, một Smart Enum có thể định nghĩa các phương thức cụ thể cho từng giá trị enum. Điều này giúp mã nguồn trở nên dễ đọc và dễ bảo trì hơn. Do đó, Smart Enums có thể cải thiện đáng kể chất lượng của mã nguồn.
Dưới đây là một ví dụ về Smart Enum trong C#:
Class SmartEnum
//SmartEnum class
public abstract class SmartEnum<TEnum, TValue> where TEnum : SmartEnum<TEnum, TValue>
{
private static readonly ConcurrentDictionary<TValue, TEnum> _items =
new ConcurrentDictionary<TValue, TEnum>();
public TValue Value { get; }
public string Name { get; }
protected SmartEnum(TValue value, string name)
{
Value = value;
Name = name;
}
static SmartEnum()
{
// Automatically register all enum values
foreach (var field in typeof(TEnum).GetFields(BindingFlags.Public | BindingFlags.Static))
{
var item = (TEnum)field.GetValue(null);
Register(item);
}
}
public static TEnum FromValue(TValue value)
{
return _items[value];
}
public static bool TryFromValue(TValue value, out TEnum result)
{
return _items.TryGetValue(value, out result);
}
public static IEnumerable<TEnum> GetAll()
{
return _items.Values;
}
protected static TEnum Register(TEnum item)
{
return _items.GetOrAdd(item.Value, item);
}
public override string ToString()
{
return Name;
}
}
//--------------------------------------------------------------
// Gender class which is inherited SmartEnum class
public class Gender : SmartEnum<Gender, string>
{
public static readonly Gender Male = new Gender("M", "Male");
public static readonly Gender Female = new Gender("F", "Female");
public static readonly Gender Unknown = new Gender("U", "Unknown");
public Gender(string value, string name)
: base(value, name)
{
}
}
//--------------------------------------------------------------
// Program.cs
using SmartEnums;
Gender gender = Gender.FromValue("M");
if (gender == Gender.Male)
{
Console.WriteLine("The person is male.");
}
Console.ReadLine();
Trong ví dụ trên, lớp SmartEnum đóng vai trò là nền tảng cho Smart Enums. Lớp này định nghĩa các thuộc tính Value và Name cho Smart Enums. Phương thức Register đảm nhiệm việc đăng ký các giá trị Smart Enum.
Phương thức FromValue ánh xạ một giá trị thành hằng số Smart Enum tương ứng. Trong khi đó, phương thức TryFromValue hoạt động tương tự nhưng có kiểm tra xem giá trị có tồn tại hay không. Lớp SmartEnum cung cấp các chức năng thiết yếu giúp triển khai Smart Enums một cách hiệu quả.
So sánh SmartEnums với Enum truyền thống
Trong phần này, chúng ta sẽ so sánh SmartEnums với Enum truyền thống thông qua các ví dụ và thảo luận về ưu nhược điểm của chúng.
1. Dễ dàng quản lý giá trị
Smart Enums giúp enum trở nên chuyên biệt hơn bằng cách cho phép bổ sung thuộc tính và phương thức, giúp phân biệt rõ ràng hơn giữa các giá trị enum và tránh lỗi lập trình.
Enum truyền thống
//Classic Enum
public enum Gender
{
Male,
Female,
Unknown
}
//Smart Enum
public class Gender : SmartEnum<Gender, string>
{
public static readonly Gender Male = new Gender("M", "Male");
public static readonly Gender Female = new Gender("F", "Female");
public static readonly Gender Unknown = new Gender("U", "Unknown");
private Gender(string value, string name)
: base(value, name)
{
}
}
2. Tăng khả năng đọc hiểu
Smart Enums giúp các hằng số enum trở nên rõ nghĩa hơn, giúp mã nguồn dễ đọc và dễ hiểu hơn.
Enum truyền thống
// Classic
public enum UserRole
{
Admin,
Manager,
Employee
}
//Smart
public class UserRole : SmartEnum<UserRole, int>
{
public static readonly UserRole Admin = new UserRole(1, "Admin");
public static readonly UserRole Manager = new UserRole(2, "Manager");
public static readonly UserRole Employee = new UserRole(3, "Employee");
private UserRole(int value, string name)
: base(value, name)
{
}
}
3. Tính linh hoạt
Smart Enums có thể chứa các thuộc tính và phương thức, giúp mã nguồn linh hoạt và dễ tái sử dụng hơn.
// Classic
public enum PaymentType
{
CreditCard,
DebitCard,
BankTransfer
}
//Smart
public sealed class PaymentType : SmartEnum<PaymentType, string>
{
public static readonly PaymentType CreditCard = new PaymentType("CC", "Credit Card", new CreditCardPaymentProcessor());
public static readonly PaymentType DebitCard = new PaymentType("DC", "Debit Card", new DebitCardPaymentProcessor());
public static readonly PaymentType BankTransfer = new PaymentType("BT", "Bank Transfer", new BankTransferPaymentProcessor());
private PaymentType(string value, string name, IPaymentProcessor paymentProcessor)
: base(value, name)
{
}
}
4. Giá trị linh hoạt
Smart Enums cho phép sử dụng các giá trị hằng số linh hoạt hơn, chẳng hạn như chuỗi hoặc ngày tháng.
public sealed class Colors : SmartEnum<Colors, string>
{
public static readonly Colors Red = new Colors("R", "Red");
public static readonly Colors Green = new Colors("G", "Green");
public static readonly Colors Blue = new Colors("B", "Blue");
private Colors(string value, string name)
: base(value, name)
{
}
}
5. Nhóm các hằng số enum
Smart Enums giúp nhóm các hằng số lại với nhau, làm mã nguồn dễ đọc hơn.
// Classic
public enum PizzaType
{
MeatLovers,
Vegetarian,
PineappleLovers
}
//Smart
public sealed class Pizza : SmartEnum<Pizza>
{
public static readonly Pizza Margherita = new Pizza(1, "Margherita", PizzaType.Vegetarian);
public static readonly Pizza Pepperoni = new Pizza(2, "Pepperoni", PizzaType.MeatLovers);
public static readonly Pizza Hawaiian = new Pizza(3, "Hawaiian", PizzaType.PineappleLovers);
public static readonly Pizza MeatLovers = new Pizza(4, "Meat Lovers", PizzaType.MeatLovers);
public static readonly Pizza VeggieLovers = new Pizza(5, "Veggie Lovers", PizzaType.Vegetarian);
public int Id { get; }
public string Name { get; }
public PizzaType Type { get; }
private Pizza(int id, string name, PizzaType type)
{
this.Id = id;
this.Name = name;
this.Type = type;
}
}
Những lợi ích chính của Smart Enums:
- Cải thiện khả năng đọc mã nguồn.
- Có thể định nghĩa các thuộc tính và hành vi tùy chỉnh cho hằng số enum.
- Giúp mã nguồn linh hoạt hơn và dễ bảo trì.
- Giải quyết các vấn đề về tính nhất quán và bảo mật trong Enum truyền thống.
Tuy nhiên, Smart Enums cũng có một số nhược điểm:
- Hạn chế hỗ trợ: Smart Enums không được hỗ trợ trực tiếp trong C#, khiến việc sử dụng trong các ngôn ngữ khác bị giới hạn.
- Đường cong học tập: Việc làm quen với Smart Enums có thể khó khăn hơn so với Enum truyền thống.
- Hiệu suất: Smart Enums tiêu tốn thêm tài nguyên bộ nhớ và xử lý.
Dù có một số nhược điểm, Smart Enums vẫn là một công cụ mạnh mẽ giúp lập trình viên viết mã dễ đọc, linh hoạt và dễ bảo trì hơn!
Chi tiết vể Smart Enum bạn có thể đọc tại đây: https://github.com/ardalis/SmartEnum