4

Possible Duplicate:
Should an Enum start with a 0 or a 1?

Why should I never use 0 in a flag enum? I have read this multiple times now and would like to know the reason :)

Community
  • 1
  • 1
Pascal
  • 12,265
  • 25
  • 103
  • 195

7 Answers7

26

Why should I never use 0 in a flag enum?

The question is predicated on an incorrect assumption. You should always use zero in a flag enum. It should always be set to mean "None".

You should never use it for anything other than to represent "none of the flags are set".

Why not?

Because it gets really confusing if zero has a meaning other than "none". One has the reasonable expectation that ((e & E.X) == E.X) means "Is the X flag set?" but if X is zero then this expression will always be true, even if logically the flag is not "set".

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
21

Because Flag enums are bit collections, or sets of options.

A 0 value would be part of all sets, or of none. It just wouldn't work.

H H
  • 263,252
  • 30
  • 330
  • 514
5

Although a zero means none of the bits are set, it is often very useful to have a named constant for 0.

When I set up flag words, I define the names of the bits so that they all represent the non-default value. That is, the enum value is always initialised to zero, turning 'off' all the options the bitfield represents.

This provides forwards compatibility for your enum, so that anyone who creates a new value knows that any zero bits are going to be 'safe' to use if you later add more flags to your bitfield.

Similarly it is very useful to combine several flags to make a new constant name, which makes code more readable.

The danger of this (and the reason for the rule you cite) is just that you have to be aware of the difference between single bit values (flags) and values that represent groups or combinations of bits.

Jason Williams
  • 56,972
  • 11
  • 108
  • 137
2

Flag enums are used like this:

Flag flags = Flag.First | Flag.Next | Flag.Last;

Then you should define your Flag like this:

enum Flag {First = 1, Next = 2, Last = 4};

This way you can see if a Flag has been used e.g.:

if (flags & Flag.First <> 0) Console.WriteLine("First is set");
if (flags & Flag.Next <> 0) Console.WriteLine("Next is set");
if (flags & Flag.Last <> 0) Console.WriteLine("Last is set");

This is why you can only use values that is a power of 2 e.g. 1,2,4,8,16,32,64,128,...

If flags is 0 then it is considered blank.

I hope that this will increase your understanding of flag enums.

Casperah
  • 4,504
  • 1
  • 19
  • 13
  • 2
    Technically, you don't have to use values that are powers of 2. For example, the values 1, 10, 100 would work just as well (because their bits don't overlap). But there is no reason to do anything like that. – svick Mar 21 '12 at 19:18
1

Because typically you use flags as follows:

var myFlagEnum = MyEnum.Foo | MyEnum.Bar | MyEnum.Bat;

// ...  snip ...

if (myFlagEnum & MyEnum.Foo == MyEnum.Foo) { ...  do something ... };

If the MyEnum.Foo were zero, the above wouldn't work (it would return true for all cases). Whereas if it were 1, then it would work.

Chris Shain
  • 50,833
  • 6
  • 93
  • 125
1

A flag enum assumes that each one of it's values represents the presence of an option and it is coded in one of the enum's bits. So if a particular option is present (or true) the equiveland bit in the enum's value is set (1), otherwise it is not set (0)

so each one of the enum's fields is a value with only one bit set. If none of the options are present or true, then the combined enum's value is zero, which mean none of the bits are set. So the only zero field in a flag's enum, is the one that supposed to mean that no option is set, true, or selected.

For example assume we have a flags enum that encodes the presence of borders in a table cell

public enum BorderType
{
    None   = 0x00, // 00000000 in binary
    Top    = 0x01, // 00000001 in binary
    Left   = 0x02, // 00000010 in binary
    Right  = 0x04, // 00000100 in binary
    Bottom = 0x08  // 00001000 in binary
}

if you want to show that a cell has the top and bottom borders present, then you should use a value of

 Cell.Border = BorderType.Top | BorderType.Bottom; // 0x01 | 0x08 = 0x09 = 00001001 in binary

if you want to show that a cell has no borders present, then you should use a value of

 Cell.Border = BorderType.None; // 0x00 = 00000000 in binary

So you should NEVER use zero as a value for an option in a flag enum, but you should always use zero as the value that means that none of the flags are set.

Thanasis Ioannidis
  • 2,981
  • 1
  • 30
  • 50
0

I really don't see the problem.

enum Where {Nowhere=0x00, Left=0x01, Right=0x02, Both=Left|Right};
Where thevalue = Where.Both;
bool result = (thevalue&Where.Nowhere)==Where.Nowhere;

Of course the result is true! What did you expect? Here, think about this.

bool result1 = (thevalue&Where.Left)==Where.Left;
bool result2 = (thevalue&Where.Right)==Where.Right;
bool result3 = (thevalue&Where.Both)==Where.Both;

These are all true! Why should Nowhere be special? There is nothing special about 0!

Mr Lister
  • 45,515
  • 15
  • 108
  • 150
  • A) The OP was about flags enums. You are only seeing flag-like behavior because the default implementation of enums is assignment of incrementing integers, and that works up to 4 entries (and only if the fourth is the union of the second and third). Try adding a fifth value. B) Nowhere is special because `nowhere & left == nowhere`, whereas `left & right == both`. – Chris Shain Mar 21 '12 at 19:22
  • OK, I'll spell out the bit values if you want. It does not make any difference though, you know. And you're still wrong about how `&` works. Look it up, or test it on your own compiler. `left & right` is really `0`. Honestly. – Mr Lister Mar 21 '12 at 19:24
  • The problem is you defined Both as Both=left|Right, not Both=Nowhere|Left|Right. So when thevalue = Where.Both but (thevalue&Where.Nowhrere)==Where.Nowhere returns true, it defeats the purpose of using this enum to represent multiple selections. It doesn't happen if you start from 0x01. May be you should think with a different example context rather than the one you used. Take days of the week for a change. – Ε Г И І И О Apr 26 '20 at 18:12
  • @MrLister What's the issue defining them as flags? If you do, you can represent multiple days using the values of the same Enum. – Ε Г И І И О Apr 27 '20 at 07:37
  • In that case, you have the same scenario as above. Sunday = bit 0, Monday = bit 1 etc. The value "never" can then be expressed as the value 0, i.e. no bits set. – Mr Lister Apr 27 '20 at 08:43
  • As long as your 0 value represents "none of the flags are set" then it's fine to have a 0 in the enum. (I think the OP just misunderstood what was meant by "using" 0). The problem would come if you attempted to "use" 0 to have a meaningful value. If `enum Where { Left=0x0, Right=0x1 };` there is no way to tell whether a value of `Where` is supposed to contain `Where.Left`, because `value & Where.Left` is always equal to `Where.Left`. Whether you test with `&` or `HasFlag()`, testing for zero will always return true, which is almost never the intended behavior. – shelleybutterfly May 26 '22 at 18:48