Why do enum permissions often have 0, 1, 2, 4 values?
Solution 1
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.
Solution 2
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.
Solution 3
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
Solution 4
[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.
Solution 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
}
Related videos on Youtube
Comments
-
Pascal about 2 years
Why are people always using enum values like
0, 1, 2, 4, 8
and not0, 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 }
-
Henk Holterman about 12 yearspossible duplicate of Enum as Flag using, setting and shifting
-
zzzzBov about 12 yearsI disagree on the dupe vote.
-
Rudy about 12 yearsUNIX way to set permission is also based on the same logic.
-
Brian about 12 years@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. -
Mosty Mostacho about 12 years@Pascal: If you don't use powers of 2, if you have, let's say
4
you wouldn't be able to differenciate between1 + 3
and a plain4
-
IAdapter about 12 yearspossible duplicate of Enum Flags Attribute
-
Jeremy S about 12 years@IAdapter 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.
-
-
Pascal about 12 yearsand what is the problem with 2|3 ?
-
Ed S. about 12 years@Pascal: Because
3
is11
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. -
Pascal about 12 yearswell 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.
-
Ed S. about 12 years@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).
-
yshavit about 12 years@Pascal put another way,
2|3 == 1|3 == 1|2 == 3
. So if you have a value with binary00000011
, and your flags included values1
,2
, and3
, then you wouldn't know if that value represents1 and 3
,2 and 3
,1 and 2
oronly 3
. That makes it a lot less useful. -
phoog about 12 years@Pascal quick, what do you get when you multiply
4194304
by 2? How about0x400000
? It's much easier to recognize0x800000
as the correct answer than8388608
, and it's also less error-prone to type the hex value. -
Brian about 12 yearsIt'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". -
Daniel Pryden about 12 years+1 for the mental image of an
enum
with four billion members. And the sad part is, probably somebody out there has tried it. -
Malcolm about 12 yearsJust 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.
-
Ed S. about 12 years@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 meh -
Andy about 12 yearsGood 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.
-
Ed S. about 12 years@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. -
Andy about 12 yearsNot quite accurate. C# may not care, but trying to use enums without Flags in VB doesn't seem to work: stackoverflow.com/questions/5902967/…
-
Ed S. about 12 years@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.
-
bevacqua about 12 yearsI'm absolutely with Jared about it being much easier to note in hex. you just use 1 2 4 8 and shift
-
detly about 12 yearsWhy not just
permissions & Permissions.Write
? (I don't really know C#, just C, so maybe it's a language difference?) -
Ed S. about 12 years@detly: Because if statements in C# require a boolean expression.
0
is notfalse
;false
isfalse
. You could however writeif((permissions & Permissions.Write) > 0)
. -
detly about 12 years@EdS. Ah, of course, I was still thinking in C, where there's no real boolean type.
-
fluffy about 12 yearsPersonally, 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).
-
fluffy about 12 years@DanielPryden As a daily reader of Daily WTF, I'd believe it.
-
Malcolm about 12 years@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.
-
user about 12 years2^33 = ~8.6 billion. For 4 billion different values you only need 32 bits.
-
ratchet freak about 12 years@MichaelKjörling one of the 33 is for the 0 default
-
Andy about 12 years@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.
-
Ed S. about 12 years@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.
-
Malcolm about 12 years@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.
-
Ed S. about 12 years@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. -
Malcolm about 12 years@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 anEnumSet<Flag> field
and you have enumFlag
andAnotherFlag
. You can't add constants fromAnotherFlag
to field, but you could if you hadint 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? -
Ed S. about 12 years@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. -
Malcolm about 12 years@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.
-
Brian about 12 years@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). -
Joel Peltonen almost 12 yearsIf 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?
-
Louis Kottmann over 11 yearsInstead of the 'tricky'
(permissions & Permissions.Write) == Permissions.Write
, you can now useenum.HasFlag()
-
Ed S. over 11 years@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" :)
-
Louis Kottmann over 11 years@EdS. Hence the quotes ;) but admit that the first time it's not so obvious.
-
ArthurV about 10 years@Nenotlep 32 positions of 1 in each position, yes, but the 33rd is all 0s, with 1 never appearing. ;-)