You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为枚举每个成员配置字典项是否属于不良设计?

问题描述

我定义了如下枚举:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 03:31:30