FlagsAttribute could’ve done a bit more


Definition of FlagsAttribute on MSDN sez “Indicates that an enumeration can be treated as a bit field; that is, a set of flags“. It further goes on to indicate “Bit fields can be combined using a bitwise OR operation, whereas enumerated constants cannot“.


Enums are generally used in situations where mutually exclusive elements are required. However, an enum can be decorated with this FlagsAttribute to indicate that they are bit flags and can also be combined (ORed, ANDed) so that it can also be used in situations where combination of elements are also valid.


When I first encountered this FlagsAttribute I assumed (devs do that a lot 🙂 ) that the values generated for these enums are no longer incremented by 1 but are bit incremented.

[Flags]

enum MyFlagEnum

{

None,

First ,

Second,

Third,

Fourth

}


So in the case of the above enum I expected that None=0, First=1, Second=2, Third = 4, Fourth=8. Only if this assumption is true we can have bit flags work correctly. Otherwise First|Second will become equal to Third and hence would be indistinguisable from Third.


However, much later I learnt with surprise that this is not true… Even if the attribute is used enum members are just incremented by one. So that Third is 3 and not 4 and Fourth is 4 and not 8.


This can have surprising consequences as demonstrated by the code below

using System;

using System.Collections.Generic;

using System.Text;

namespace FlagAttribute

{

[Flags]

enum MyFlagEnum

{

None,

First ,

Second,

Third,

Fourth

}

class Program

{

static void Main(string[] args)

{

foreach (MyFlagEnum myEnum in
Enum.GetValues(typeof(MyFlagEnum)))

{

Console.WriteLine(“{0,-10} => {1}”, myEnum,
myEnum.ToString(
“D”));

}

Console.WriteLine((MyFlagEnum.First | MyFlagEnum.Second)
==
MyFlagEnum.Third);

Console.WriteLine(MyFlagEnum.First | MyFlagEnum.Second);

Console.WriteLine(MyFlagEnum.Second | MyFlagEnum.Fourth);

}

}

}

None       => 0
First      => 1
Second     => 2
Third      => 3
Fourth     => 4
True
Third
Second, Fourth

The problem here is that the value of First|Second is 3 and hence there is no way to distinguish it from MyFlagEnum.Third. However in case of  Second|Fourth the value is 6 and hence the sytem correctly printed it to Second, Fourth.


This can be corrected only by manually giving correct values to the enum members as…

[Flags]

enum MyFlagEnum

{

None = 0,

First = 1,

Second = 2,

Third = 4,

Fourth = 8

}


So what did we gain from the FlagsAttribute, not much I guess…

Comments (6)

  1. Doug says:

    Attributes only affect the metadata.  To auto-increment the bit field, flag would need to be a keyword instead of an attribute.  The attribute must be used somewhere other than at compile time.  Does it affect the property grid?

  2. hagay says:

    I perosnally thinks its a bad idea for the enviorment (like visual studio) to generate the Enum values automaticly & implicitly

    since sometimes we store the enum data to a disk or something & the value might change since we added an enum value to our ENUM and the enviorment will generate the values differently now…

  3. Adam says:

    IMO, a collection of bitfields should not even be an enum with an extra attribute. A collection of bitfields is not an enumeration. An enumeration enumerates all the possible values that a variable of that type can be assigned. A collection of bitfields does not. The values (and particularly the collection of bits) behind the elements of an enumeration should be irrelevant and encapsulated, as long as they are all different. Similarly, the actual bits (and particularly the values they represent if cast to an integer) used for a collection of fields should be irrelevant and encapsulated, as long as they are all different.[0]

    The semantics for enumerations and collections of bits are completely different, and really ought to be kept that way.

    If they’d added:

    flagset MyFlagSet {

       first,

       second,

       third,

       fourth

    };

    instead of semi-overloading enum, that would make a lot more sense.

    Note that "none" should not be required for a flagset as it is implicitly a value that can be assigned to all variables of that type, indicating that no flags have been selected. There should either be another special value (‘none’) that can be applied to all flagsets, or  a literal 0 should be able to be assigned to a flagset.

    [0] Yes, enum values do need be stable to ensure that future additions are not API/ABI breaking, but aside from that, the underlying values should not matter.

  4. Max Stafford says:

    what we got: it says right in the definition:

    Bit fields can be combined using a bitwise OR operation, whereas enumerated constants cannot

    I agree that using enums to emulate flags is really lame.. Sets are better and more logical.. but… it worked just fine in c/c++.. why fix something if it’s not broke?

  5. Mike Dimmick says:

    If you don’t have [Flags] specified on an enum, the compiler will warn you when you try to | (OR) them together.

    You can have values that aren’t powers of 2 because there are a number of Windows flags-type enumerations which have multivalued fields – using 2 bits and assigning different meanings to the four different values of those two bits.

  6. MikeT says:

    The [Flags] shouldn't change the increment as if you enumerate the flag set you need to specify all possible values so

    [Flags]

    public enum myFlag

    {

    Flag1,

    Flag2,

    Flag1and2

    Flag3

    Flag1and3

    Flag2and3

    Flag1and2and3

    }

    the only thing the [flags] check is doing is making it possible to just check for flag 1 2 or 3 with out have to build a massive and repetitive conditional statement