Alex Rivera | Logout

Why does [Flag]'d enums start at 0 and increment by 1?

Asked 2010-06-24T17:03:06.027
9

Edit: It seems most people misunderstood my question.

I know how enum works, and I know binary. I'm wondering why the enums with the [Flags] attribute is designed the way it is.

Original post:

This might be a duplicate, but I didn't find any other posts, so here goes.

I bet there has been some good rationale behind it, I just find it a bit bug prone.

[Flag]
public enum Flagged
{
  One, // 0
  Two, // 1
  Three, // 2
  Four, // 3
}

Flagged f; // Defaults to Flagged.One = 0
f = Flagged.Four;
(f & Flagged.One) != 0; // Sure.. One defaults to 0
(f & Flagged.Two) != 0; // 3 & 1 == 1
(f & Flagged.Three) != 0; // 3 & 2 == 2

Wouldn't it have made more sense if it did something like this?

[Flag]
public enum Flagged
{
  One = 1 << 0, // 1
  Two = 1 << 1, // 2
  Three = 1 << 2, // 4
  Four = 1 << 3, // 8
}

Flagged f; // Defaults to 0
f = Flagged.Four;
(f & Flagged.One) != 0; // 8 & 1  == 0
(f & Flagged.Two) != 0; // 8 & 2 == 0
(f & Flagged.Three) != 0; // 8 & 4 == 0
(f & Flagged.Four) != 0; // 8 & 8 == 8

Of course.. I'm not quite sure how it should handle custom flags like this

[Flag]
public enum Flagged
{
   One, // 1
   Two, // 2
   LessThanThree = One | Two,
   Three, // 4? start from Two?
   LessThanFour = Three | LessThanThree,
   Three, // 8? start from Three?
}

The spec gives some guidelines

Define enumeration constants in powers of two, that is, 1, 2, 4, 8, and so on. This means the individual flags in combined enumeration constants do not overlap.

But this should perhaps be done automatically as I bet you would never want my first example to occur. Please enlighten me :)

Edit
Report

1 Answer

0

In C, it's possible to (ab)use the preprocessor to generate power-of-two enumerations automatically. If one has a macro make_things which expands to "make_thing(flag1) make_thing(flag2) make_thing(flag3)" etc. it's possible to invoke that macro multiple times, with different definitions of make_thing, so as to achieve a power-of-two sequence of flag names as well as some other goodies.

For example, start by defining make_thing(x) as "LINEAR_ENUM_##x," (including the comma), and then use an enum statement to generate a list of enumerations (including, outside the make_things macro, LINEAR_NUM_ITEMS). Then create another enumeration, this time with make_thing(x) defined as "FLAG_ENUM_##x = 1<

Rather nifty some of the things that can be done that way, with flag and linear values automatically kept in sync; code can do nice things like "if (thingie[LINEAR_ENUM_foo] thing_errors |= FLAG_ENUM_foo;" (using both linear and flag values). Unfortunately, I know of no way to do anything remotely similar in C# or VB.net.

answered 2010-06-24T18:30:27.320

Your Answer