Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c#

enums

bitflags

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

like image 230
simendsjo Avatar asked Jun 24 '10 17:06

simendsjo


People also ask

Do enums always start at 0?

If the first enumerator has no initializer, the value of the corresponding constant is zero. An enumerator-definition without an initializer gives the enumerator the value obtained by increasing the value of the previous enumerator by one. So yes, if you do not specify a start value, it will default to 0.

Do C# enums start at 0 or 1?

The first member of an enum will be 0, and the value of each successive enum member is increased by 1. You can assign different values to enum member.

What is the role of flag attribute in enums?

The [Flag] attribute is used when Enum represents a collection of multiple possible values rather than a single value. All the possible combination of values will come. The [Flags] attribute should be used whenever the enumerable represents a collection of possible values, rather than a single value.

Are enums 0 based?

Rule description The default value of an uninitialized enumeration, just like other value types, is zero.


5 Answers

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.

like image 174
Brian Rasmussen Avatar answered Oct 21 '22 08:10

Brian Rasmussen


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.

like image 38
Jeff Yates Avatar answered Oct 21 '22 09:10

Jeff Yates


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.

like image 28
BC. Avatar answered Oct 21 '22 09:10

BC.


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.

like image 21
Jon Skeet Avatar answered Oct 21 '22 08:10

Jon Skeet


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.

like image 38
Powerlord Avatar answered Oct 21 '22 08:10

Powerlord