Why do enum permissions often have 0, 1, 2, 4 values?
Asked Answered
T

7

165

Why are people always using enum values like 0, 1, 2, 4, 8 and not 0, 1, 2, 3, 4?

Has this something to do with bit operations, etc.?

I would really appreciate a small sample snippet on how this is used correctly :)

[Flags]
public enum Permissions
{
    None   = 0,
    Read   = 1,
    Write  = 2,
    Delete = 4
}
Theretofore answered 21/3, 2012 at 19:0 Comment(7)
possible duplicate of Enum as Flag using, setting and shiftingWarplane
I disagree on the dupe vote.Halmstad
UNIX way to set permission is also based on the same logic.Momentary
@Pascal: You may find it helpful to read about Bitwise OR (and Bitwise AND ), which is what | (and &) represent. The various answers assume you are familiar with it.Pustule
@Pascal: If you don't use powers of 2, if you have, let's say 4 you wouldn't be able to differenciate between 1 + 3 and a plain 4Snowbird
possible duplicate of Enum Flags AttributeEdgardo
@Edgardo I can see why you'd think that, as the answer to both are the same, but I think the questions are different. The other question just asks for an example or explanation of the Flags attribute in C#. This question seems to be about the concept of bit flags, and the fundamentals behind them.Erde
F
272

Because they are powers of two and I can do this:

var permissions = Permissions.Read | Permissions.Write;

And perhaps later...

if( (permissions & Permissions.Write) == Permissions.Write )
{
    // we have write access
}

It is a bit field, where each set bit corresponds to some permission (or whatever the enumerated value logically corresponds to). If these were defined as 1, 2, 3, ... you would not be able to use bitwise operators in this fashion and get meaningful results. To delve deeper...

Permissions.Read   == 1 == 00000001
Permissions.Write  == 2 == 00000010
Permissions.Delete == 4 == 00000100

Notice a pattern here? Now if we take my original example, i.e.,

var permissions = Permissions.Read | Permissions.Write;

Then...

permissions == 00000011

See? Both the Read and Write bits are set, and I can check that independently (Also notice that the Delete bit is not set and therefore this value does not convey permission to delete).

It allows one to store multiple flags in a single field of bits.

Faa answered 21/3, 2012 at 19:1 Comment(20)
Just as a side note, in Java a collection of enums is where EnumSet comes in handy instead of a bit field. C# probably has an analogue, maybe it's worth mentioning in the answer.Coastland
@Malcolm: It does; myEnum.IsSet. I am of the opinion that this is a completely useless abstraction and serves only to cut down typing, but mehFaa
Good answer, but you should mention why the Flags attribute is applied, and when you wouldn't want to apply Flags to some enums as well.Dupleix
@Andy: Actually, the Flags attribute does little more than give you 'pretty printing' iirc. You can use an enumerated value as a flag regardless of the presence of the attribute.Faa
Not quite accurate. C# may not care, but trying to use enums without Flags in VB doesn't seem to work: #5903467Dupleix
@Andy: Well, I don't use VB, so I wouldn't know. I gave a C# answer because the question is tagged C#, but it is interesting that they would differ.Faa
Why not just permissions & Permissions.Write? (I don't really know C#, just C, so maybe it's a language difference?)Clue
@detly: Because if statements in C# require a boolean expression. 0 is not false; false is false. You could however write if((permissions & Permissions.Write) > 0).Faa
@EdS. Ah, of course, I was still thinking in C, where there's no real boolean type.Clue
@EdS. Josh Bloch doesn't agree with you in Item 32 of the Effective Java. It is not only less typing, but also less error-prone, and easier to use because instead of working with bitwise operations you work with collections.Coastland
@EdS. Understood, but while the question is about C# that doesn't mean the poster isn't doing things that need to be CLS complaint and visible in other languages.Dupleix
@Malcolm: Well, whether Josh Bloch agrees with me or not, this is a personal opinion, and I don't see the value in hiding such a simple operation behind a method. That operation is never going to change, and honestly, any half way decent programmer should be able to AND a couple of variables without issue. I don't buy the "less error prone" argument.Faa
@EdS. To AND a few constants is not a problem, but what about adding a range of flags, iterating over a set of flags, or printing it? And also nothing prevents from adding a wrong constant to a bit field, because there's no type checking. So I see no benefits in using bit fields except when you need to work with serialization, for example. This is just a suggestion, of course, the answer is yours, and you don't have to add something based on my opinion.Coastland
@Malcolm: I guess I don't understand. Adding a range of flags? Ok; flags = Flag.First | Flag.Second | Flag.Third. Not hard or any more error prone than calling a method. How does a method prevent you from adding the wrong value? I also don't understand this; "So I see no benefits in using bit fields except when you need to work with serialization" - Really? You would rather define a property on a type for every possible value? I don't get it.Faa
@EdS. Adding a range of flags: that's for three of them, what if you have much more? Try to replace EnumSet.range(Flag.FIRST, Flag.FOURTEENTH). Prevention of wrong values: you have an EnumSet<Flag> field and you have enum Flag and AnotherFlag. You can't add constants from AnotherFlag to field, but you could if you had int field. As for the properties, you don't need to define any properties, you only replace the bit field with an EnumSet. Maybe I didn't quite get your point, can you elaborate?Coastland
@Malcolm: OK, I misunderstood you initially as I don't know Java. I thought BitSet(somFlag) was a function that returned a boolean depending on whether or not the value was present, i.e., if(permissions.BitSet(someValue)). It is actually representative of a set of bits, where more than just the check above is abstracted away from you.Faa
@EdS. Yes, exactly. In turn, I'm not really familiar with C#, so that's why I don't know whether my suggestion based on Java is valid here. Glad that we've cleared up this misunderstanding.Coastland
Instead of the 'tricky' (permissions & Permissions.Write) == Permissions.Write, you can now use enum.HasFlag()Pack
@Baboon: Yes, it's more consise which is nice, but you'll have to try harder to convince me that basic bitwise operations are "tricky" :)Faa
@EdS. Hence the quotes ;) but admit that the first time it's not so obvious.Pack
L
151

If it is still not clear from the other answers, think about it like this:

[Flags] 
public enum Permissions 
{   
   None = 0,   
   Read = 1,     
   Write = 2,   
   Delete = 4 
} 

is just a shorter way to write:

public enum Permissions 
{   
    DeleteNoWriteNoReadNo = 0,   // None
    DeleteNoWriteNoReadYes = 1,  // Read
    DeleteNoWriteYesReadNo = 2,  // Write
    DeleteNoWriteYesReadYes = 3, // Read + Write
    DeleteYesWriteNoReadNo = 4,   // Delete
    DeleteYesWriteNoReadYes = 5,  // Read + Delete
    DeleteYesWriteYesReadNo = 6,  // Write + Delete
    DeleteYesWriteYesReadYes = 7, // Read + Write + Delete
} 

There are eight possibilities but you can represent them as combinations of only four members. If there were sixteen possibilities then you could represent them as combinations of only five members. If there were four billion possibilities then you could represent them as combinations of only 33 members! It is obviously far better to have only 33 members, each (except zero) a power of two, than to try to name four billion items in an enum.

Lederer answered 21/3, 2012 at 19:54 Comment(7)
+1 for the mental image of an enum with four billion members. And the sad part is, probably somebody out there has tried it.Lashaunda
@DanielPryden As a daily reader of Daily WTF, I'd believe it.Heroics
2^33 = ~8.6 billion. For 4 billion different values you only need 32 bits.Sizar
@MichaelKjörling one of the 33 is for the 0 defaultStarwort
@MichaelKjörling: To be fair, there are only 32 members which are powers of 2, since 0 is not a power of two. So "33 members, each one a power of two" is not precisely correct (unless you count 2 ** -infinity as a power of two).Pustule
If using bitwise operations wouldn't it limit the amount of enum members to 32 assuming we start at 1? The 32nd member would have the (binary) value 10000000000000000000000000000000. N members would equal N bits or am I incorrect?Rheinlander
@Nenotlep 32 positions of 1 in each position, yes, but the 33rd is all 0s, with 1 never appearing. ;-)Vishinsky
G
37

Because these values represent unique bit locations in binary:

1 == binary 00000001
2 == binary 00000010
4 == binary 00000100

etc., so

1 | 2 == binary 00000011

EDIT:

3 == binary 00000011

3 in binary is represented by a value of 1 in both the ones place and the twos place. It is actually the same as the value 1 | 2. So when you are trying to use the binary places as flags to represent some state, 3 isn't usually meaningful (unless there is a logical value that actually is the combination of the two)

For further clarification, you might want to extend your example enum as follows:

[Flags]
public Enum Permissions
{
  None = 0,   // Binary 0000000
  Read = 1,   // Binary 0000001
  Write = 2,  // Binary 0000010
  Delete = 4, // Binary 0000100
  All = 7,    // Binary 0000111
}

Therefore in I have Permissions.All, I also implicitly have Permissions.Read, Permissions.Write, and Permissions.Delete

Gussiegussman answered 21/3, 2012 at 19:2 Comment(3)
and what is the problem with 2|3 ?Theretofore
@Pascal: Because 3 is 11 binary, i.e., it does not map to a single set bit, so you lose the ability to map 1 bit in an arbitrary position to a meaningful value.Faa
@Theretofore put another way, 2|3 == 1|3 == 1|2 == 3. So if you have a value with binary 00000011, and your flags included values 1, 2, and 3, then you wouldn't know if that value represents 1 and 3, 2 and 3, 1 and 2 or only 3. That makes it a lot less useful.Twayblade
M
11
[Flags]
public Enum Permissions
{
    None   =    0; //0000000
    Read   =    1; //0000001
    Write  = 1<<1; //0000010
    Delete = 1<<2; //0000100
    Blah1  = 1<<3; //0001000
    Blah2  = 1<<4; //0010000
}

I think writing using a binary shift operator << is easier to understand and read, and you don't need to calculate it.

Marston answered 1/4, 2012 at 8:21 Comment(0)
A
5

These are used to represent bit flags which allows combinations of enum values. I think it's clearer if you write the values in hex notation

[Flags]
public Enum Permissions
{
  None =  0x00,
  Read =  0x01,
  Write = 0x02,
  Delete= 0x04,
  Blah1 = 0x08,
  Blah2 = 0x10
}
Awl answered 21/3, 2012 at 19:3 Comment(6)
well int "16" is more readable than hex "0x10" for me with int I know its always the double value 2 to 4, 4 to 8, 8 to 16, 16 to 32 etc.. with hex I do not know that, maybe also I am not firm with hex values.Theretofore
@Pascal: Perhaps it is more readable to you at this point in time, but as you gain experience viewing bytes in hex becomes second nature. Two digits in hex maps to one byte maps to 8 bits (well... a byte is usually 8 bits anyway... not always true, but for this example it is ok to generalize).Faa
@Theretofore quick, what do you get when you multiply 4194304 by 2? How about 0x400000? It's much easier to recognize 0x800000 as the correct answer than 8388608, and it's also less error-prone to type the hex value.Beguin
It's a lot easier to tell, at a glance, if your flags are set properly (i.e., are powers of 2), if you use hex. Is 0x10000 a power of two? Yes, it starts with 1, 2, 4, or 8 and has all 0s afterwards. You do not need to mentally translate 0x10 to 16 (though doing so will probably become second nature eventually), just think of it as, "some power of 2".Pustule
I'm absolutely with Jared about it being much easier to note in hex. you just use 1 2 4 8 and shiftGrater
Personally, I prefer just using e.g. P_READ=1<<0, P_WRITE=1<,1, P_RW = P_READ|P_WRITE. I'm not sure if that sort of constant-folding works in C# but it works just fine in C/C++ (as well as Java, I think).Heroics
A
1

This is really more of a comment, but since that wouldn't support formatting, I just wanted to include a method I've employed for setting up flag enumerations:

[Flags]
public enum FlagTest
{
    None = 0,
    Read = 1,
    Write = Read * 2,
    Delete = Write * 2,
    ReadWrite = Read|Write
}

I find this approach especially helpful during development in the case where you like to maintain your flags in alphabetical order. If you determine you need to add a new flag value, you can just insert it alphabetically and the only value you have to change is the one it now precedes.

Note, however, that once a solution is published to any production system (especially if the enum is exposed without a tight coupling, such as over a web service), then it is highly advisable against changing any existing value within the enum.

Amnesty answered 27/3, 2012 at 19:28 Comment(0)
B
1

Lot's of good answers to this one… I'll just say.. if you do not like, or cannot easily grasp what the << syntax is trying to express.. I personally prefer an alternative (and dare I say, straightforward enum declaration style)…

typedef NS_OPTIONS(NSUInteger, Align) {
    AlignLeft         = 00000001,
    AlignRight        = 00000010,
    AlignTop          = 00000100,
    AlignBottom       = 00001000,
    AlignTopLeft      = 00000101,
    AlignTopRight     = 00000110,
    AlignBottomLeft   = 00001001,
    AlignBottomRight  = 00001010
};

NSLog(@"%ld == %ld", AlignLeft | AlignBottom, AlignBottomLeft);

LOG 513 == 513

So much easier (for myself, at least) to comprehend. Line up the ones… describe the result you desire, get the result you WANT.. No "calculations" necessary.

Biondo answered 25/4, 2013 at 13:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.