Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enum bit fields that support inheritance

I apologize for the Title of this Question, but I couldn't think of a better way to describe what I'm trying to accomplish..

I want to make an Enum using the Flags attribute whereby the bitwise values of the enum create somewhat of an inheritance schema.

Example: Say we have a bit field enum of food:

[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,    
}

Notice how the enum contains sub-categories of food as well as sub-sub-categories. In my real-world scenario, I have an unknown number of top-level categories and an unknown number of sub-level categories (including an unknown level of sub-sub-sub-n-categories)

I would like to set the bit values of the enum such that sub-categories (and sub-sub-n-categories) will "inherit" the bit value of its immediate parent.

I am wanting to avoid the client setter of the Foods property to have to explicitly set all flags for a particular food. In other words, I do not want the client setter to have to do something like:

myClass.Foods = Foods.Tritip | Foods.Beef | Foods.Meat;

I am trying to create a construct so that the client setter simply has to set one flag and all parents of that flag are implicit within the child flag.

So if the client setter did this:

myClass.Foods = Foods.Tritip;

We would be able to run the following conditions successfully:

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!");

What confuses me about this problem is what bit values to use for each food item in the enum. I initially started out with:

[Flags]
    enum Foods
    {
        // Top Level foods
        Meat = 0x1,
        Fruit = 0x2,
        Vegetables = 0x4,
        Dairy = 0x8,
        Grain = 0x10,
        BenJerrysOatmealCookieChunk = 0x20, // ;)

        ...and so on.
     }

.. but since I don't know how many levels there will be (the enum list will grow over time).. I don't know how to create my bit values so that they allow for growth.

Has anyone attempted to do this before? If so, what was your business logic for your bit values?

I have a sneaky feeling this Question will need a bounty ;)

like image 547
Jed Avatar asked Sep 13 '26 12:09

Jed


1 Answers

You could do this by doing bitwise OR inside of the enum declaration itself. While this will not give you a nice clean ToString like you normally get with the Flags attribute, it does give you the desired result:

[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,  
}

I used ulong for the enum's base so that you get more space for subcategories. I didn't start at the highest order bit possible, but if I were you, I would so that there would be room to represent deeper categories. Also, you'd want to play around with the spacing between sub-categories to yield the optimal results.

like image 90
Abe Heidebrecht Avatar answered Sep 15 '26 08:09

Abe Heidebrecht



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!