C# 枚举支持继承的位字段
对于这个问题的标题,我深表歉意,但我想不出更好的方式来描述我正在努力实现的目标 我想使用Flags属性创建一个枚举,通过该属性,枚举的按位值可以创建某种继承模式 示例:假设我们有一个食物的位字段枚举:C# 枚举支持继承的位字段,c#,inheritance,enums,bit-manipulation,bit,C#,Inheritance,Enums,Bit Manipulation,Bit,对于这个问题的标题,我深表歉意,但我想不出更好的方式来描述我正在努力实现的目标 我想使用Flags属性创建一个枚举,通过该属性,枚举的按位值可以创建某种继承模式 示例:假设我们有一个食物的位字段枚举: [Flags] enum Foods { // Top Level foods Meat, Fruit, Vegetables, Dairy, Grain, BenJerrysOatmealCookieChunk, // ;) //
[Flags]
enum Foods
{
// Top Level foods
Meat,
Fruit,
Vegetables,
Dairy,
Grain,
BenJerrysOatmealCookieChunk, // ;)
// sub-Meat
Chicken,
Beef,
Fish,
// sub-Fruit
Apple,
Banana,
// sub-Vegetables
Spinach,
Broccoli,
// sub-Dairy
Cheese,
Milk,
// sub-Grain
Bread,
Pasta,
Rice,
// sub-Beef
Tritip,
Hamburger,
// sub-Fish
Salmon,
Halibut,
}
请注意枚举如何包含食物的子类别以及子类别。在我的现实世界场景中,我有未知数量的顶级类别和未知数量的子级别类别(包括未知级别的子n类)
我希望设置枚举的位值,以便子类别(和sub-sub-n-categories)将“继承”其直接父级的位值
我希望避免Foods属性的客户端设置程序必须显式地设置特定食物的所有标志。换句话说,我不希望客户端setter必须执行以下操作:
myClass.Foods = Foods.Tritip | Foods.Beef | Foods.Meat;
我试图创建一个构造,以便客户端setter只需设置一个标志,并且该标志的所有父项都隐式地包含在子标志中
因此,如果客户机setter这样做:
myClass.Foods = Foods.Tritip;
我们将能够成功运行以下条件:
if((myClass.Foods & Foods.Meat) == Foods.Meat)
Console.WriteLine("I am meat");
if((myClass.Foods & Foods.Beef) == Foods.Beef)
Console.WriteLine("I am beef");
if((myClass.Foods & Foods.Tritip) == Foods.Tritip)
Console.WriteLine("I am tritip");
if((myClass.Foods & Foods.Meat) == Foods.Meat)
Console.WriteLine("I am meat");
if((myClass.Foods & Foods.Rice) != Foods.Rice)
Console.WriteLine("I am definitely not rice!");
关于这个问题,让我困惑的是枚举中每个食物项使用什么位值。我最初是从以下方面开始的:
[Flags]
enum Foods
{
// Top Level foods
Meat = 0x1,
Fruit = 0x2,
Vegetables = 0x4,
Dairy = 0x8,
Grain = 0x10,
BenJerrysOatmealCookieChunk = 0x20, // ;)
...and so on.
}
。。但由于我不知道会有多少级别(枚举列表将随着时间的推移而增长)。。我不知道如何创建我的比特值,以便它们允许增长
以前有人尝试过这样做吗?如果是的话,您的比特值的业务逻辑是什么
我隐约觉得这个问题需要悬赏;) 这是不可能的。枚举不能从其他枚举继承。事实上,所有枚举实际上都必须从System.Enum继承。C#允许语法更改枚举值的底层表示形式,这看起来像是继承,但实际上它们仍然从System.enum继承 有关完整详细信息,请参见CLI规范第8.5.2节。规范中的相关信息
- 所有枚举必须从System.Enum派生
- 由于上述原因,所有枚举都是值类型,因此是密封的
但是,您可以使用基类和派生类实现所需的功能。您可以通过按位或在枚举声明本身内部执行来实现这一点。虽然这不会像通常使用Flags属性时那样为您提供一个干净的ToString,但它确实会为您提供所需的结果:
[Flags]
public enum Foods : ulong
{
Meat = 0x100000,
Fruit = 0x200000,
Vegetables = 0x400000,
Dairy = 0x800000,
Grain = 0xF00000,
Beef = Meat | 0x100,
Chicken = Meat | 0x200,
Fish = Meat | 0x400,
Apple = Fruit | 0x100,
Banana = Fruit | 0x200,
Spinach = Vegetables | 0x100,
Broccoli = Vegetables | 0x200,
Cheese = Dairy | 0x100,
Milk = Dairy | 0x200,
Bread = Grain | 0x100,
Pasta = Grain | 0x200,
Rice = Grain | 0x400,
Tritip = Beef | 0x1,
Hamburger = Beef | 0x2,
Salmon = Fish | 0x100,
Halibut = Fish | 0x200,
}
我使用ulong作为枚举的基础,以便为子类别获得更多空间。我并没有尽可能从最高级别开始,但如果我是你,我会这样做,这样就有空间来代表更深层的类别。此外,您还需要调整子类别之间的间距,以产生最佳结果。OP说(我引用)
请注意枚举如何包含食物的子类别以及子类别。
在我的真实场景中,我有未知数量的顶级类别和
未知数量的子级别类别(包括未知级别的
子类别)
那么您就没有枚举了:您有某种类型的层次结构。枚举(按设计)封装某种整型,并具有固定数量的元素。事实上,它们基本上是定点整数,这就为可用位数设置了上限:枚举的大小为8位、16位、32位或64位
给定这些限制枚举,您可以执行如下操作(并且您不需要或不想要[Flags]
属性)。在下面的示例中,我为“类别”保留了32位,为“子类别”保留了32位
请注意,为其类别查询Foods
值需要进行一些微调,如下所示:
Foods item = Foods.Hamburger ;
Foods category = (Foods) ( (ulong)item & (ulong)Foods.CategoryMask ) ;
我不会在凌晨2:30冲出家门去买Ben&Jerry's。当你知道你可以拥有任意数量的元素时,尝试用Enum来做这件事有点疯狂。我们大多数人都使用一个元素树,这些元素之间具有层次关系,通常存储在关系数据库或XML或其他形式中
myclass.HasAncestorCategory(“Meat”)
更简单。仅供参考-我不想继承枚举类型。我正在尝试继承位值。在这种情况下,“继承”是一个误导性的术语,但它在某种程度上描述了我的意图。示例:按位值0x12“继承”0x2,即((0x2&0x12)=(0x2))的计算结果为true。
Foods item = Foods.Hamburger ;
Foods category = (Foods) ( (ulong)item & (ulong)Foods.CategoryMask ) ;