为枚举每个成员配置字典项是否属于不良设计?
问题描述
我定义了如下枚举:
enum Product{ Knife, Fork, Spoon }
并创建了一个对应枚举每个成员的字典,用于存储商品价格:
var productPrices = new Dictionary<Product, double>(){ {Product.Knife, 10}, {Product.Fork, 10}, {Product.Spoon, 15} };
该字典需包含所有商品的价格。我会遍历所有枚举成员获取价格:
foreach(Product p in Enum.GetValues(typeof(Product))){ var price = productPrices[p]; }
若字典中缺少某个枚举成员的条目,代码会抛出错误。这种字典与枚举强耦合(无正式关联)的设计是否属于不良设计?比如新增枚举成员时,需同步添加字典条目。若是不良设计,有什么更好的替代方案?
回答
这种设计确实属于不良设计,核心问题在于枚举和字典之间没有强制绑定约束,完全依赖人工同步维护,很容易出问题:
- 新增枚举成员时忘记补字典条目,运行时会触发
KeyNotFoundException - 删除枚举成员时没清理字典,留下冗余数据
- 字典里可能出现枚举中不存在的无效键,导致逻辑混乱
替代方案
1. 用自定义特性给枚举绑定价格
把价格直接通过特性绑定到枚举成员上,从根源消除耦合:
先定义自定义特性:
[AttributeUsage(AttributeTargets.Field)] public class PriceAttribute : Attribute { public double Price { get; } public PriceAttribute(double price) { Price = price; } }
给枚举成员标记特性:
enum Product{ [Price(10)] Knife, [Price(10)] Fork, [Price(15)] Spoon }
再写个工具方法读取价格:
public static double GetProductPrice(Product product) { var fieldInfo = typeof(Product).GetField(product.ToString()); var priceAttr = fieldInfo.GetCustomAttribute<PriceAttribute>(); if(priceAttr == null) throw new InvalidOperationException($"未给枚举成员{product}配置价格"); return priceAttr.Price; }
使用时直接调用这个方法就行,新增枚举成员时必须加特性(否则读取时会抛出明确错误,比字典的运行时异常更容易排查)。
2. 用类替代枚举(复杂场景推荐)
如果商品除了价格还有其他属性(比如名称、库存),用类封装更灵活:
public class Product { public static readonly Product Knife = new Product(10); public static readonly Product Fork = new Product(10); public static readonly Product Spoon = new Product(15); public double Price { get; } private Product(double price) { Price = price; } // 获取所有商品的方法 public static IEnumerable<Product> GetAllProducts() { yield return Knife; yield return Fork; yield return Spoon; } }
使用时直接访问静态实例的Price属性:
foreach(var product in Product.GetAllProducts()){ var price = product.Price; }
这种方式彻底消除了耦合,新增商品时只要加新的静态实例并更新GetAllProducts方法就行,不会遗漏,还能轻松扩展其他属性。
3. 初始化字典后校验完整性
如果一定要保留字典,可以在初始化后加校验逻辑,确保所有枚举成员都有对应价格:
var productPrices = new Dictionary<Product, double>(){ {Product.Knife, 10}, {Product.Fork, 10}, {Product.Spoon, 15} }; // 校验所有枚举成员都在字典中 foreach(var product in Enum.GetValues(typeof(Product)).Cast<Product>()){ if(!productPrices.ContainsKey(product)){ throw new InvalidOperationException($"枚举成员{product}未在价格字典中配置"); } }
这样能在程序启动时就发现遗漏问题,不用等到遍历的时候才抛异常,提前暴露错误。
内容的提问来源于stack exchange,提问作者user3221037
相关产品推荐
相关产品推荐

