【问题标题】:Mapping enum to "sub-enum"将枚举映射到“子枚举”
【发布时间】:2014-10-20 18:03:31
【问题描述】:

我有一个包含产品的数据库。 这些产品是分类的,有一个Category和一个Subcategory

例如:

Product p1 = new Product()
{ 
    Category = Category.Fruit, 
    Subcategory = Subcategory.Apple 
 };

我的问题是我想根据类别限制子类别。

下面的例子应该是不可能的:

Product p2 = new Product()
{ 
    Category = Category.Fruit, 
    Subcategory = Subcategory.Cheese 
};

此外,我希望能够返回一个字符串数组(匹配每个 Category 枚举),每个字符串都有一个相应子类别的数组。

想了半天也没想到,网上也没找到解决办法。

会有什么建议?

【问题讨论】:

  • 除非您为不同的子类别组使用单独的枚举,否则没有编译时方法可以执行此操作。您可以通过多种不同的方式在运行时进行验证。
  • 继承层次结构可能更适合您的要求
  • 这是业务逻辑,不是纯技术解决方案。基本上,类别与子类别必须在某个地方(数据库/代码)定义,并且必须进行查找。
  • 让产品有一个由每个类别实现的抽象类
  • 这很老套,但如果你赶时间,你可以将 Category 和 SubCategory 连接成一个枚举:enum Cats{ Fruit_Apple, Fruit_Pear }

标签: c# .net enums


【解决方案1】:

我喜欢地图规则。 您还可以在枚举值上添加自定义属性。

例如:

public enum Subcategory {
    [SubcategoryOf(Category.Fruit)]
    Apple,
    [SubcategoryOf(Category.Dairy)]
    Emmenthaler
}

这要求您编写一个SubcategoryOfAttribute 类(参见here 以获取MS 指南)。然后你可以编写一个验证器,它可以查看任何子类别并从中获取合法的父类别。

与地图相比,这种关系的优势在于声明中很好地阐明了这种关系。

缺点是每个子类最多可以有一个父类。

我觉得这很有趣,所以我把它删掉了。首先是属性:

[AttributeUsage(AttributeTargets.Field)]
public class SubcategoryOf : Attribute {
    public SubcategoryOf(Category cat) {
        Category = cat;
    }
    public Category Category { get; private set; }
}

然后我们做一些模拟枚举

public enum Category {
    Fruit,
    Dairy,
    Vegetable,
    Electronics
}

public enum Subcategory {
    [SubcategoryOf(Category.Fruit)]
    Apple,
    [SubcategoryOf(Category.Dairy)]
    Buttermilk,
    [SubcategoryOf(Category.Dairy)]
    Emmenthaler,
    [SubcategoryOf(Category.Fruit)]
    Orange,
    [SubcategoryOf(Category.Electronics)]
    Mp3Player
}

现在我们需要一个谓词来确定一个子类别是否与一个类别匹配(注意:如果需要,您可以拥有多个父类别 - 您需要修改属性和此谓词以获取所有属性和检查每一个。

public static class Extensions {
    public static bool IsSubcategoryOf(this Subcategory sub, Category cat) {
        Type t = typeof(Subcategory);
        MemberInfo mi = t.GetMember(sub.ToString()).FirstOrDefault(m => m.GetCustomAttribute(typeof(SubcategoryOf)) != null);
        if (mi == null) throw new ArgumentException("Subcategory " + sub + " has no category.");
        SubcategoryOf subAttr = (SubcategoryOf)mi.GetCustomAttribute(typeof(SubcategoryOf));
        return subAttr.Category == cat;
    }
}

然后你输入你的产品类型来测试它:

public class Product {
    public Product(Category cat, Subcategory sub) {
        if (!sub.IsSubcategoryOf(cat)) throw new ArgumentException(
            String.Format("{0} is not a sub category of {1}.", sub, cat), "sub");
        Category = cat;
        Subcategory = sub;
    }

    public Category Category { get; private set; }
    public Subcategory Subcategory { get; private set; }
}

测试代码:

Product p = new Product(Category.Electronics, Subcategory.Mp3Player); // succeeds
Product q = new Product(Category.Dairy, Subcategory.Apple); // throws an exception

【讨论】:

  • 非常感谢 - 这就是我最终使用的。效果很好!
【解决方案2】:

您要做的是使用枚举表示一阶逻辑 (http://en.wikipedia.org/wiki/First-order_logic)。并与数据库同步。在代码中硬编码时,这不是一件容易的事。已经提出了许多好的解决方案。

就我而言,我只会对 Category 和 SubCategory 使用字符串(或唯一 ID),并使用数据库中定义的规则强制执行完整性。但是如果你最终在代码中使用它,它就不会是编译时的。

Enum 的问题在于它必须与您的外部源代码和代码相匹配。此外,附加更多信息变得很困难,例如价格或国家/地区,或者即使您有不同种类的苹果。

【讨论】:

    【解决方案3】:

    我的建议是有一个Dictionary<SubCategory, Category>,它将您的SubCategory 映射到您的Category

    之后,您可以一起删除产品上的Category,或者您可以使用辅助方法

    public class Product
    {
        static Dictionary<SubCategory, Category> _categoriesMap;
    
        public static Product()
        {
            _categoriesMap = new Dictionary<SubCategory, Category>();
            _categoriesMap.Add(SubCategory.Apple, Category.Fruit);
        }
    
        public SubCategory SubCategory { get; set; }
    
        public Category Category
        {
            get { return _categoriesMap[this.SubCategory]; }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2019-08-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-08
      相关资源
      最近更新 更多