凡可列舉者,皆應被命名,用數字或字串記錄分類,像在沙地上寫字。風吹就散,誰都能改,誰都會迷路。 唯有列舉(Enum),能為混亂命名,能讓程式碼之神歸位,如編鐘之列、星辰之座。 有些人會說,Enum 限制了自由,但真相是:「沒有邊界的自由,終究會走向混亂。」
說到這裡,應該不難理解,Enum 的本質,可以減少混亂值、提升可讀性、強化型別安全,杜絕魔法數字與野生字串,我們會說,有結構的程式碼,是對未來同事最好的善意。 當你看到一個架構龐大的 C# 專案,結果一個收藏 Enum 的資料夾都沒有時,個人認為,隔天就可以提交離職信了(當天先去收個驚)
我們來定義一下它存在的意義
Enum 用來代表「一組固定、有限、離散的值」,
讓我們的程式碼更有語意、更安全、更容易維護。
舉個例子 處理訂單的狀態時(待處理、處理中、完成、取消),當我們 hardcode 字串在做判斷的話會長這樣
1 2 3 4 5 6 7 8 if (status == "待處理" ){ Console.WriteLine("訂單尚未處理。" ); }
更好的做法是,定義一組訂單狀態列表,大家遵循或修改都依照這個列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public enum OrderStatus{ Pending = 0 , Processing = 1 , Completed = 2 , Canceled = 3 } if (status == OrderStatus.Pending){ Console.WriteLine("訂單尚未處理。" ); }
Description 有時候,我們會希望 Enum 可以對應一段更具描述性的資訊。這可以透過在 Enum 上掛 Description Attribute 來實現。
藉由引用 System.ComponentModel.DescriptionAttribute namespace 可以使用 [Description(“”]
例如傳簡訊
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 void Main (){ var desc = SMSTemplateEnum.RegisterMember.GetDescription(); Console.WriteLine(desc); } public static class EnumExtensions { public static string GetDescription (this Enum value ) { Type type = value .GetType(); var memberInfo = type.GetMember(value .ToString()); if (memberInfo != null && memberInfo.Length > 0 ) { var attrs = memberInfo[0 ].GetCustomAttributes(typeof (DescriptionAttribute), false ); if (attrs != null && attrs.Length > 0 ) { return ((DescriptionAttribute)attrs[0 ]).Description; } } return value .ToString(); } } public enum SMSTemplateEnum{ [Description("會員註冊" ) ] RegisterMember = 0 , [Description("官網導下載" ) ] OfficialToAppDownLoad = 1 , [Description("會員卡管理" ) ] MembershipCard = 3 , }
Enum 當作查詢主鍵,對應到處理器 我們把用 Enum 來「判斷」,升格到 「查表」,查的是規則、邏輯、設定、服務、函式,甚至整段流程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 public enum UserType{ Normal, VIP, New } public interface IUserDiscountStrategy : IDiscountStrategy { UserType UserType { get ; } } public class VIPStrategy : IUserDiscountStrategy { public UserType UserType => UserType.VIP; public decimal CalculateDiscount (decimal price ) => price * 0.8 m; } public class NormalStrategy : IUserDiscountStrategy { public UserType UserType => UserType.Normal; public decimal CalculateDiscount (decimal price ) => price; } public class NewUserStrategy : IUserDiscountStrategy { public UserType UserType => UserType.New; public decimal CalculateDiscount (decimal price ) => price * 0.9 m; } builder.Services.AddTransient<IUserDiscountStrategy, NormalStrategy>(); builder.Services.AddTransient<IUserDiscountStrategy, VIPStrategy>(); builder.Services.AddTransient<IUserDiscountStrategy, NewUserStrategy>(); builder.Services.AddSingleton<DiscountStrategyFactory>(); public class DiscountStrategyFactory { private readonly Dictionary<UserType, IUserDiscountStrategy> _strategyMap; public DiscountStrategyFactory (IEnumerable<IUserDiscountStrategy> strategies ) { _strategyMap = strategies.ToDictionary(s => s.UserType); } public IDiscountStrategy GetStrategy (UserType userType ) { if (_strategyMap.TryGetValue(userType, out var strategy)) { return strategy; } throw new NotSupportedException($"不支援的用戶類型:{userType} " ); } } var factory = serviceProvider.GetRequiredService<DiscountStrategyFactory>();var userType = UserType.VIP;var price = 1000 m;var strategy = factory.GetStrategy(userType);var finalPrice = strategy.CalculateDiscount(price);Console.WriteLine($"計算後價格:{finalPrice} " );
如此一來,比起用 switchcase 去判斷,寫一堆邏輯,我們更傾向用封裝好的服務或方法會更乾淨
結語 正如人生中,良好的規矩不是束縛,而是保護。寫程式不是只有快,而是留下可被理解、被擴展、被信任的邏輯。 Enum 看似微小,卻是尊重邏輯、尊重團隊、尊重未來的證明。每一個 Enum,就像種下一棵可以遮風避雨的樹。 它不是最華麗的語法,卻用來建築有溫度的家。
某日有開發者問我: 「這不過是一個小小的 enum,何必那麼認真?」
我微笑回答: 「你所謂的小小 enum,是他日我所依靠的大樹陰涼。」