Sunday, June 10, 2012

Why do enum permissions often have 0, 1, 2, 4 values


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 how this is used correctly :)




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


Source: Tips4all

6 comments:

  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.

    ReplyDelete
  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 one a power of two, than to try to name four billion items in an enum.

    ReplyDelete
  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

    ReplyDelete
  4. 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;
    }

    ReplyDelete
  5. [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 write like this is easier to understand and read,And you don't need to calculate it.

    ReplyDelete
  6. 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
    }

    ReplyDelete