将枚举映射到"子枚举"

时间:2014-10-20 18:03:31

标签: c# .net enums

我有一个包含产品的数据库。 这些产品经过分类,具有类别子类别

例如:

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

我的问题是我想限制子类别,具体取决于类别。

以下示例不可能:

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

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

我已经考虑了一段时间,但没有提出任何建议,也没有在网上找到任何解决方案。

会有什么建议?

3 个答案:

答案 0 :(得分:5)

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

例如:

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

这需要您编写SubcategoryOfAttribute课程(有关MS指南,请参阅here)。然后,您可以编写一个可以查看任何子类别并从中获取合法父类别的验证程序。

这对地图的好处是很好地说明了这种关系。

缺点是每个子类别最多只能有一个父类别。

我发现这很有趣,所以我把它弄清楚了。首先是属性:

[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

答案 1 :(得分:3)

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

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

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

答案 2 :(得分:2)

我的建议是设置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]; }
    }
}