tags:

views:

192

answers:

3

I have the following enum:

[Flags]
    public enum PermissionLevel {
        User = 1,
        Administrator = 2,
        ITStaff = 3,
        Manager = 4,
        SuperAdministrator = 6,
    }

When I do:

PermissionLevel permission = (PermissionLevel) dr.GetInt32(i);

I get random permission values assigned to the permission object. For instance, if i is 6, my permission object returns "Administrator | Manager" and I'm supposed to get "SuperAdministrator". When I cast the instance back to integer it returns 6.

Am I missing something?

+10  A: 

You need to make sure that each combination of values is unique:

[Flags]
public enum PermissionLevel {
    User = 1,
    Administrator = 2,
    ITStaff = 4,
    Manager = 8,
    SuperAdministrator = 16
}

As your enum currently looks, 6 can mean either SuperAdministrator or Administrator | Manager. 4 can be either Manager or User | ITStaff and so on.

Fredrik Mörk
Is this specified on the documentation?
Raúl Roa
Yes, in the documentation for the Flags attribute: http://msdn.microsoft.com/en-us/library/system.flagsattribute.aspx
Fredrik Mörk
There's an easy way to avoid this problem: Don't use 1,2,4,8. Use 1<<1, 1<<2, 1<<3, 1<<4, ...
Jason Williams
@Frederik, This wrong, combination values are perfectly normal. In fact, on the msdn docs page it says " Consider creating an enumerated constant for commonly used flag combinations. For example, if you have an enumeration used for file I/O operations that contains the enumerated constants Read = 1 and Write = 2, consider creating the enumerated constant ReadWrite = Read OR Write, which combines the Read and Write flags. In addition, the bitwise OR operation used to combine the flags might be considered an advanced concept in some circumstances that should not be required for simple tasks."
Charles Bretana
@Charles: Sensible combinations maybe, but certainly not what he has in the question. And generally if you're doing a combination, you should use the predefined *name* not a numeric number, to make it obvious it's a combination. Jason't suggestion is quite good and quite valid.
ctacke
without any intention to start a protracted discussion, imho, 1, what is "sensible" is a) subjective and open for discussion, and b) a domain model matter, not a technical matter, which is not resolvable given the info in the question. And 2. more specifically, The answer above, as it reads, implies that using this technique is *in general* wrong, which it is not.
Charles Bretana
And the sample code included in the amnswer, by making the ITStaff and SuperAdministrator values separate powers of 2, is *incorrect*, in that it no longer models the intent to represent *either one or the other* of the base values...
Charles Bretana
@Jason what problem do you avoid exactly by using that?
Raúl Roa
@Charles: that is your interpretation. I simply answered the question, in which the problem was exactly that the values were not unique. I frequently use combined values in enums myself, but that was not applicable in this case (which I first thought when skimming through the question).
Fredrik Mörk
@Fredrik, The "problem" mentioned in the question is not that the values are not unique, it is that the cast to a permission object generates the string "Administrator | Manager" instead of "SuperAdministrator".
Charles Bretana
@Fredrik, With your sample code, casting 6 to the enum - (PermissionLevel) 6; will still return "Administrator | Manager", which is now (using yr code) not the same as "SuperAdministrator" and, therefore, is wrong.
Charles Bretana
@Charles: I think your missing the OP problem. He specifically stated: "if i is 6, my permission object returns "Administrator | Manager" and I'm supposed to get "SuperAdministrator"" In other words, for his application "Administrator|Manager" != SuperAdministrator.
Chris Dunaway
@Charles: where do you find the requirement that "SuperAdministrator" should be the same as "Administrator | Manager"? As I interpret the original question, that behavior was what was wrong, not a desired behavior.
Fredrik Mörk
@Frederik, I get it from the fact that in the question, he sets it to the value of 6, which is a bitwise combination of Manager and Administrator...
Charles Bretana
@Frederik, the fact that his enum type is defined with "SuperAdministrator" set to a value of 6 says this... That alone unequivically implies that he means this value to represent the Managers and the Administrators...
Charles Bretana
@Charles: does it also make sense to you that the combination of User and ITStaff is the same as Manager? At the same time as ITStaff also happens to be the same as the combination of User and Administrator. To me it seems clear that the problem was that the OP simply had not understood how Flags enum work.
Fredrik Mörk
@Frederik, No, frankly, I confess that I don't understand the business model interpretation for that, but, again, that's a domain model issue, not a technical one. But the fact that he wrote the enum to express it as a combination value tells me that the domain model intention is for it to be represented that way.
Charles Bretana
@Frederik, maybe in this domain model, (I'm making this up as an example), all the individuals who must be represented in this application include users, non-Users, and Administrators can be a user or a nonuser, and the application functionality needs to behave differently in a specific scenario when it is passed a individual who is either a user or an non-user Administrator...
Charles Bretana
+4  A: 

There's nothing random about it. If permission is 6, the binary value is 110. There are two active flags (bits with a value of 1), the bit with the value of 4 (Manager), and 2 (Administrator). The SuperAdministrator value of 6 that you set is actually a combination of Manager and Administrator.

Sometimes that's the behavior that you want, but it doesn't seem to be your case. You should then change your enum so that each value represents a unique bit, as other answers demonstrate.

Meta-Knight
Thanks, your explanation was very useful to understand the behavior of this type of enum
Raúl Roa
+2  A: 

when decorated with the Flags attribute, you can do what you are doing and it's perfectly fine.. The CLR is telling you that the value assigned to the enum instance is and Administrator Or a Manager... thats what the vertical pipe is, a bitwise Or operator.
From the msdn page msdn Flags Attribute

"Consider creating an enumerated constant for commonly used flag combinations. For example, if you have an enumeration used for file I/O operations that contains the enumerated constants Read = 1 and Write = 2, consider creating the enumerated constant ReadWrite = Read OR Write, which combines the Read and Write flags. In addition, the bitwise OR operation used to combine the flags might be considered an advanced concept in some circumstances that should not be required for simple tasks."

You could have just as easily have declared the enum as follows:

   [Flags]public enum PermissionLevel 
    {        
       User = 1,        
       Administrator = 2,        
       ITStaff = User | Administrator,        
       Manager = 4,        
       SuperAdministrator = Administrator | Manager ,    
    }

and oh, btw, it's considered good practice to always include a None value...

    [Flags]public enum PermissionLevel 
    { 
       None = 0,       
       User = 1,        
       Administrator = 2,        
       ITStaff = User | Administrator,        
       Manager = 4,        
       SuperAdministrator = Administrator | Manager ,    
    }

This technique is extremely useful, as it allows client code to test a candidate value against a subset of the individual values using syntax that concisely expresses the actual business intent... assuming candValue is one of core individual values...

 if ((candValue & PermissionLevel.SuperAdministrator) == candVal) 
     // tests to see if candValue is Administrator Or Manager

which would require 2 comparisons if you did not have access to an enum value that represented the SuperAdministrator bitmask = 00000110

Charles Bretana