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

6

8

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 :)

Archduke answered 24/6, 2010 at 17:3 Comment(3)
1 << 1 is equal to 2 as the 1 is shifted by one position to the left! 1 << 0 is equal to 1.Jackquelin
Ouch. Stupid mistake :/Archduke
You asked one of the many question I want to ask. I also prefer the language itself provide the auto sq value for Flags. It also work if the dev team add a constructor to enum or provide a way to set a formula to define the value of the enum. For now, we have to do it manually. sad.Bukharin
A
6

The attribute is [Flags] not [Flag] and there's nothing magical about it. The only thing it seems to affect is the ToString method. When [Flags] is specified, the values come out comma delimited. It's up to you to specify the values to make it valid to be used in a bit field.

Aquatic answered 24/6, 2010 at 17:13 Comment(0)
H
13

The Flags attribute is only used for formatting the values as multiple values. The bit operations work on the underlying type with or without the attribute.

Harijan answered 24/6, 2010 at 17:10 Comment(0)
T
7

The first item of an enumeration is zero unless explicitly given some other value. It is often best practice to have a zero value for flags enumerations as it provides a semantic meaning to the zero value such as "No flags" or "Turned off". This can be helpful in maintaining code as it can imply intent in your code (although comments also achieve this).

Other than that, it really is up to you and your design as to whether you require a zero value or not.

As flag enumerations are still just enumerations (the FlagsAttribute merely instructs the debugger to interpret the values as combinations of other values), the next value in an enumeration is always one more than the previous value. Therefore, you should be explicit in specifying the bit values as you may want to express combinations as bitwise-ORs of other values.

That said, it is not unreasonable to imagine a syntax for flags enumerations that demands all bitwise combinations are placed at the end of the enumeration definition or are marked in some way, so that the compiler knows how to handle everything else.

For example (assuming a flags keyword and that we're in the northern hemisphere),

flags enum MyFlags
{
   January,
   February,
   March,
   April,
   May,
   June,
   July,
   August,
   September,
   October,
   November,
   December,
   Winter = January | February | March
   Spring = April | May | June
   Summer = July | August | September
   Autumn = October | November | December
} 

With this syntax, the compiler could create the 0 value itself, and assign flags to the other values automatically.

Transilient answered 24/6, 2010 at 17:5 Comment(2)
Note that your quote only says that there should always be a zero value for non-flags enums. Personally I disagree with even that...Alumroot
@Jon: Updated to remove all that rubbish :) Thanks for keeping me honest.Transilient
A
6

The attribute is [Flags] not [Flag] and there's nothing magical about it. The only thing it seems to affect is the ToString method. When [Flags] is specified, the values come out comma delimited. It's up to you to specify the values to make it valid to be used in a bit field.

Aquatic answered 24/6, 2010 at 17:13 Comment(0)
A
4

There's nothing in the annotated C# 3 spec. I think there may be something in the annotated C# 4 spec - I'm not sure. (I think I started writing such an annotation myself, but then deleted it.)

It's fine for simple cases, but as soon as you start adding extra flags, it gets a bit tricky:

[Flags]
enum Features
{
    Frobbing, // 1
    Blogging, // 2 
    Widgeting, // 4
    BloggingAndWidgeting = Frobbing | Blogging, // 6
    Mindnumbing // ?
}

What value should Mindnumbing have? The next bit that isn't used? What about if you set a fixed integer value?

I agree that this is a pain. Maybe some rules could be worked out that would be reasonable... but I wonder whether the complexity vs value balance would really work out.

Alumroot answered 24/6, 2010 at 17:12 Comment(2)
A reasonable and simple rule would be to disable automatic numbering for [Flags] completely.Schmid
If there is something broken in C#, it's the enums (now that Tuples exist). The syntax to enumerate enum values is ugly, there is no way to enforce restrictions, etc...Niel
F
2

Simply put, Flags is an attribute. It doesn't apply until after the enumeration is created, and thus doesn't change the values assigned to the enumeration.

Having said that, the MSDN page Designing Flags Enumerations says this:

Do use powers of two for a flags enumeration's values so they can be freely combined using the bitwise OR operation.

Important: If you do not use powers of two or combinations of powers of two, bitwise operations will not work as expected.

Likewise, the page for the FlagsAttribute says

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.

Flied answered 24/6, 2010 at 18:20 Comment(0)
B
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.

Bihari answered 24/6, 2010 at 18:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.