tags:

views:

174

answers:

6

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 One
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 << 1, // 1
  Two = 1 << 2, // 2
  Three = 1 << 3, // 4
  Four = 1 << 4, // 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 :)

+6  A: 

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.

Jeff Yates
You can always just use 0 for that though...
Jon Skeet
@Jeff: Wanna bet? Try it :)
Jon Skeet
Note that your quote only says that there should always be a zero value for non-flags enums. Personally I disagree with even that...
Jon Skeet
@Jon: Updated to remove all that rubbish :) Thanks for keeping me honest.
Jeff Yates
+7  A: 

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.

Brian Rasmussen
+2  A: 

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.

Jon Skeet
A reasonable and simple rule would be to disable automatic numbering for `[Flags]` completely.
Pavel Minaev
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...
SWeko
+5  A: 

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.

BC
+3  A: 

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.

R. Bemrose
A: 
supercat