Author Topic: Bitwise setting storage  (Read 9540 times)

0 Members and 1 Guest are viewing this topic.

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
Bitwise setting storage
« on: May 14, 2013, 03:10:36 PM »
In this new project I am developing, I am having to rewrite everything from ground up, including all of the internal settings for what to display and hide based on user settings.

I have categorized the various controls into groups and I would like to control their visibility and/or enabled status using boolean properties of my User class.

For example:

Code - C#: [Select]
  1. //sample class
  2. public class User {
  3.  
  4.     private const uint SETTINGS_PROPERTIES_VIEW = 1;
  5.     private const uint SETTINGS_PROPERTIES_EDIT = 2;
  6.  
  7.     private uint _settings = 0;
  8.  
  9.     public User(uint Settings)
  10.     {
  11.         _settings = Settings;
  12.     }
  13.  
  14.     public bool CanEditProperty
  15.     {
  16.         get
  17.         {
  18.             return (_settings && SETTINGS_PROPERTIES_EDIT) > 0 ;
  19.         }
  20.     }
  21.     public bool CanViewProperty
  22.     {
  23.         get
  24.         {
  25.             return (_settings && SETTINGS_PROPERTIES_VIEW) > 0 ;
  26.         }
  27.     }
  28.  
  29. }
  30.  

Then in my form use something like this:
Code - C#: [Select]
  1. //user control to display and/or disable
  2.    TextBox1.Enabled = _user.CanEditProperty;
  3.    TextBox1.Visible = _user.CanViewProperty;
  4.  

So, the question I have is twofold ... first, is this a simple enough way to manage the process; second, considering that some of the permission groups have nearly 30 settings, I will need a 32 bit integer. Should I stick with an unsigned int32 for the value or should I use something bigger. My gut tells me that I don't need it, so use of int64 would just be a waste of memory resources, not that there is a huge number of these being passed around.

Also, considering that ulong and uint32 are both 4 bytes is there any advantage/disadvantage to using one type over the other?
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

TheMaster

  • Guest
Re: Bitwise setting storage
« Reply #1 on: May 15, 2013, 12:18:16 AM »
In this new project I am developing, I am having to rewrite everything from ground up, including all of the internal settings for what to display and hide based on user settings.

I have categorized the various controls into groups and I would like to control their visibility and/or enabled status using boolean properties of my User class.

For example:

Code - C#: [Select]
  1. //sample class
  2. public class User {
  3.  
  4.     private const uint SETTINGS_PROPERTIES_VIEW = 1;
  5.     private const uint SETTINGS_PROPERTIES_EDIT = 2;
  6.  
  7.     private uint _settings = 0;
  8.  
  9.     public User(uint Settings)
  10.     {
  11.         _settings = Settings;
  12.     }
  13.  
  14.     public bool CanEditProperty
  15.     {
  16.         get
  17.         {
  18.             return (_settings && SETTINGS_PROPERTIES_EDIT) > 0 ;
  19.         }
  20.     }
  21.     public bool CanViewProperty
  22.     {
  23.         get
  24.         {
  25.             return (_settings && SETTINGS_PROPERTIES_VIEW) > 0 ;
  26.         }
  27.     }
  28.  
  29. }
  30.  

Then in my form use something like this:
Code - C#: [Select]
  1. //user control to display and/or disable
  2.    TextBox1.Enabled = _user.CanEditProperty;
  3.    TextBox1.Visible = _user.CanViewProperty;
  4.  

So, the question I have is twofold ... first, is this a simple enough way to manage the process; second, considering that some of the permission groups have nearly 30 settings, I will need a 32 bit integer. Should I stick with an unsigned int32 for the value or should I use something bigger. My gut tells me that I don't need it, so use of int64 would just be a waste of memory resources, not that there is a huge number of these being passed around.


Why not just use an enum type, as that's what they're for (with the [Flags] attribute ?

Code - C#: [Select]
  1.  
  2.    [Flags]
  3.    public enum SettingsProperties  // or whatever name you want to give it.
  4.    {
  5.        CanView = 1,
  6. [      CanEdit = 2
  7.    }
  8.  
  9.  

In .NET 3.5 later, you can use the Enum.HasFlags() method to
return a bool indicating if a flag (or combination of flags) is set
in an Enum type, eliminating the long-winded 'flags = flags & bit'
business.

Quote
Also, considering that ulong and uint32 are both 4 bytes is there any advantage/disadvantage to using one type over the other?

Both ulong and UInt32 are not cls-compliant, and should be avoided unless you are dealing with them in conjunction with calling native methods that use those types via P/Invoke.
« Last Edit: May 15, 2013, 12:23:49 AM by TT »

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
Re: Bitwise setting storage
« Reply #2 on: May 15, 2013, 08:52:41 AM »
good information, thanks, but I have a couple of followup questions.

I'll still have to code the values of the enum because they need to be incremented as 2^n, and because that is the case, when I have a setting, lets say its value is 3, and that value does not have an equivalent enum previously defined, does this still work?
Code - C#: [Select]
  1. SettingProperties someval = 3;
  2. someval.hasFlag(enum1);
  3.  

Are there any potential problems treating an undefined enum integer value as an enum or will it not matter, just as long as we are defining the variable as an enum type?

So, instead of unsigned integer, I should just use Int32 just as long as my enum values don't exceed 2^30.
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

exmachina

  • Guest
Re: Bitwise setting storage
« Reply #3 on: May 15, 2013, 11:06:09 AM »
With 30 settings you can use a unsigned 32-bit integer without any problem

Code - C#: [Select]
  1. [Flags]
  2. internal enum SettingsEnum : uint {
  3.     flag_0 = 0,
  4.     flag_1 = 1,
  5.     flag_2 = 2,
  6.     flag_3 = 4,
  7.     flag_4 = 6,
  8.     flag_5 = 8,
  9.     flag_6 = 0x10,
  10.     flag_7 = 0x20,
  11.     flag_8 = 0x40,
  12.     flag_9 = 0x80,
  13.     flag_10 = 0x100,
  14.     flag_11 = 0x200,
  15.     flag_12 = 0x400,
  16.     flag_13 = 0x800,
  17.     flag_14 = 0x1000,
  18.     flag_15 = 0x2000,
  19.     flag_16 = 0x4000,
  20.     flag_17 = 0x8000,
  21.     flag_18 = 0x10000,
  22.     flag_19 = 0x20000,
  23.     flag_20 = 0x40000,
  24.     flag_21 = 0x80000,
  25.     flag_22 = 0x100000,
  26.     flag_23 = 0x200000,
  27.     flag_24 = 0x400000,
  28.     flag_25 = 0x800000,
  29.     flag_26 = 0x1000000,
  30.     flag_27 = 0x2000000,
  31.     flag_28 = 0x4000000,
  32.     flag_29 = 0x8000000,
  33.     flag_30 = 0x10000000,
  34.     flag_31 = 0x20000000,
  35.     flag_32 = 0x40000000,
  36.     flag_33 = 0x80000000
  37. }
  38.  
  39.  
  40. Private SettingsEnum _settings;
  41.  
  42. public bool BooleanSetting32 {
  43.     get {
  44.         return (_settings & SettingsEnum.flag_33) != SettingsEnum.flag_0;
  45.     }
  46.     set {
  47.         if (value)
  48.             _settings |= SettingsEnum.flag_33;
  49.         else
  50.             _settings &= ~SettingsEnum.flag_33;
  51.     }
  52. }

8 settings (This method can be used with a byte array):
Code - C#: [Select]
  1. private byte _bitsettings;
  2.  
  3. public bool LowerBooleanSetting {
  4.     get {
  5.         return BitTest(_bitsettings, 0);
  6.     }
  7.     set {
  8.         if (value)
  9.             _bitsettings = BitSet(_bitsettings, 0);
  10.         else
  11.             _bitsettings = BitClear(_bitsettings, 0);
  12.     }
  13. }
  14.  
  15. public bool UpperBooleanSetting {
  16.     get {
  17.         return BitTest(_bitsettings, 7);
  18.     }
  19.     set {
  20.         if (value)
  21.             _bitsettings = BitSet(_bitsettings, 7);
  22.         else
  23.             _bitsettings = BitClear(_bitsettings, 7);
  24.     }
  25. }
  26.  
  27. private bool BitTest(byte num, int bit) {
  28.     return (num & (1 << bit)) != 0;
  29. }
  30.  
  31. private byte BitSet(byte num, int bit) {
  32.     return (byte)(num | (1 << bit));
  33. }
  34.  
  35. private byte BitClear(byte num, int bit) {
  36.     return (byte)(num & ~(1 << bit));
  37. }

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Bitwise setting storage
« Reply #4 on: May 15, 2013, 12:29:00 PM »
good information, thanks, but I have a couple of followup questions.

I'll still have to code the values of the enum because they need to be incremented as 2^n, and because that is the case, when I have a setting, lets say its value is 3, and that value does not have an equivalent enum previously defined, does this still work?
If you use enums as bitwise then the values should have meaning where they can be combined and not a list a values that should be exclusive.
 
For example a value of 3 in below example would mean user can open and copy file.
And HasFlags will return true for CanCopy & CanOpen
 
The following will print to console
Quote

True
True
False
Code - C#: [Select]
  1.  
  2.  using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. namespace ConsoleApplication10
  7. {
  8.     [Flags]
  9.     public enum Settings
  10.     {
  11.         CanCopy = 1,
  12.         CanOpen = 2,
  13.         CanEdit = 4,
  14.         CanDelete = 8
  15.     }
  16.     class Program
  17.     {
  18.         static void Main(string[] args)
  19.         {
  20.             Settings set = Settings.CanCopy | Settings.CanOpen;
  21.             Console.WriteLine(set.HasFlag(Settings.CanCopy));
  22.             Console.WriteLine(set.HasFlag(Settings.CanOpen));
  23.             Console.WriteLine(set.HasFlag(Settings.CanEdit));
  24.             Console.Read();
  25.         }
  26.     }
  27. }
  28.  
  29.  

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
Re: Bitwise setting storage
« Reply #5 on: May 15, 2013, 01:11:28 PM »
Jeff, I understand what you are saying and I know about the values being exclusive. The point I was making is that there isn't a matching enum for 3 in the enum values because they are all factors of 2 .. in effect:

Code - C#: [Select]
  1. //instantiating a new user class with an integer value from the database, stored as Int32
  2. //assume GetPermissionsValueFromDatabase() returns 3
  3. User newUser = new User(GetPermissionsValueFromDatabase());
  4.  
  5. //Constructor
  6. User(Setting set)
  7. {
  8.     _set = set;
  9. }
  10.  
  11.  

I guess I just don't feel right about declaring an enum and assigning it an enum value that doesn't exist (ex. there is no enum with a value of 3).
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

TheMaster

  • Guest
Re: Bitwise setting storage
« Reply #6 on: May 15, 2013, 03:37:09 PM »
Jeff, I understand what you are saying and I know about the values being exclusive. The point I was making is that there isn't a matching enum for 3 in the enum values because they are all factors of 2 .. in effect:

I don't think you understand what Jeff was saying, because in the following sentence and the point you are trying to make clearly suggests otherwise.

The value 3 in your example is the bitwise logical-or of bits 1 and 2, so assigning a value of 3 means that you are assigning values to multiple bits simultaneously:

Code - C#: [Select]
  1.  
  2.     [Flags]
  3.     public Enum MyFlags
  4.     {
  5.          None = 0,
  6.          First = 1,
  7.          Second = 2
  8.     }
  9.  
  10.  

In the above a value of 3 is the result of '(MyFlags.First | MyFlags.Second)' (or MyFlags.First Or MyFlags.Second in VB).

Hence:

Code - C#: [Select]
  1.  
  2.     (int) (MyFlags.First | MyFlags.Second) == 3;  // returns true
  3.  
  4.  

So, I don't really understand the point you were trying to make, because in a bitwise-encoded value, all possible values can represent either a single bit, or a bitwise combination of multiple bits, and it is a contradiction to have enum values that correspond to anything but a single bit, because that is the purpose of using boolean operators (And, Or, Xor, and Not) to combine them.

Code - C#: [Select]
  1.  
  2.     MyFlags flags = MyFlags.First | MyFlags.Second;   // assigns the value of 3 to flags
  3.  
  4.  

Code - Visual Basic: [Select]
  1.  
  2.    Dim flags As MyFlags = MyFlags.First Or MyFlags.Second   '' assigns the value 3 to flags
  3.  
  4.  

So, the only way I can fathom how one would not 'feel right' about assigning an enum value that doesn't exist to an enum type, is if they didn't understand the basic concept underlying boolean logic and using a single integer to represent a set of multiple true/false values, and manipulating individual bits and combinations thereof is done.

And for that matter (using the MyFlags example Enum above again...):

Code - C#: [Select]
  1.  
  2.     MyFlags flags = (MyFlags.First | MyFlags.Second);     // assigns the value 3 to flags
  3.  
  4.     MyFlags flags2 = (MyFlags) 3;      // assigns the value 3 to flags2
  5.  
  6.  

Both of the above lines assign the same value to each of the two variables, the first of which is textbook C# (the vb equivalent using 'Or' in place of the | operator), so by 'not feel right' are you saying that George Boole had it all wrong from the beginning?


[edit: corrected incorrect operator usage]
« Last Edit: May 15, 2013, 07:58:32 PM by TT »

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Bitwise setting storage
« Reply #7 on: May 15, 2013, 05:42:49 PM »
Code - C#: [Select]
  1.  
  2.     (int) (MyFlags.First & MyFlags.Second) == 3;  // returns true
  3.  
  4.  

You're saying that bitwise AND of 1 and 2 is 3.. i.e. (01) & (10) = (11)? I would think that evaluates to (00)
Do you mean to use the  | operator?

:lmao: corrected by the time I posted my reply...
Think we need a facepalm emoticon on here! :lmao:

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
Re: Bitwise setting storage
« Reply #8 on: May 15, 2013, 05:45:45 PM »
What does George Boole have to do with whether or not an undefined enum can exist?

Code - C#: [Select]
  1. //
  2. //there are no enum values in the definition that equate to 0,3,5,7,9,11,13 or 15
  3. //they are achieved through bitwise operations
  4. [Flags]
  5. public enum MyEnum
  6. {
  7.      read = 1,
  8.      write = 2,
  9.      create = 4,
  10.      delete = 8
  11. }
  12.  
  13.  

Let me try this:

Are enums essentially integers that we can assign any arbitrary but valid value to, even if it isn't defined at designtime? Or are we basically saying when we declare enums that *thesevariables* equal *thesevalues* and MyEnum someVal = 15 is just as valid as (if not more appropriate than) this:

Code - C#: [Select]
  1. //
  2. //rwcd = 15 is redundant and unnecessary
  3. //
  4. [Flags]
  5. public enum MyEnum
  6. {
  7.      read = 1,
  8.      write = 2,
  9.      create = 4,
  10.      delete = 8,
  11.      rwcd = 15
  12. }
  13.  
  14.  


Incidently, I have finished coding the class in which this is used. I ended up with 17 different enums (I was able to reduce it from 23) and 174 values in those 17 enums. All told there are over 200 permissions that this will be used to manage.
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Bitwise setting storage
« Reply #9 on: May 15, 2013, 06:41:32 PM »
Enums are baked in and are symbolic constants, and all values have to be defined at design time.
 
You can add convenience values like FileShare from System.IO
 
 
Code - C#: [Select]
  1.  
  2.  [Serializable, ComVisible(true), Flags]
  3. public enum FileShare
  4. {
  5.     Delete = 4,
  6.     Inheritable = 0x10,
  7.     None = 0,
  8.     Read = 1,
  9.     ReadWrite = 3,
  10.     Write = 2
  11. }
  12.  
  13.  

CADbloke

  • Bull Frog
  • Posts: 342
  • Crash Test Dummy
Re: Bitwise setting storage
« Reply #10 on: May 15, 2013, 07:31:53 PM »
I did a similar thing to this at 4am this morning (I have a 2 yo) but I used static readonly int to avoid any casting and still use boolean bitwise comparisons. See http://stackoverflow.com/q/943398/492 for enum vs int and http://stackoverflow.com/a/755693/492 for why I didn't use a const.

While we're on the subject of ANDs and ORs, make sure you are clear on the difference between logical and boolean operators and also that || "short-circuits" an evaluation and may be quicker. See http://stackoverflow.com/q/1279217/492 and the linked questions for clarity.

[edit ... an update to clarify why I went with the int type at 4am, added after TT's answer below. I believe his methodology is more correct than mine. Actually, I can't think of a circumstance where I wouldn't believe that.]

In my case the flags were used as a field in a SortedList<string, int> which I bound to a dropdown list on a form. The list items' permissions are a combination of flags in each item. There's probably a way in my use-case to do it "properly" but I am yet to get around to figuring it out. This works for now.

Here's a sanitised version of how I used it ...
Code - C#: [Select]
  1. internal static class MyFlags
  2. {
  3.     internal static readonly int Read = 1  ;
  4.     internal static readonly int Write = 2  ;
  5.     internal static readonly int Create = 4  ;
  6.     internal static readonly int Delete = 8  ;
  7.     internal static readonly int RWCD = Read | Write | Create | Delete;
  8. }
  9.  
  10. // usage
  11.  
  12. if (ThisUser.Permission & MyFlags.Read >0) // ... let them read;

Note that in the usage I still use the field names and not the actual integer. That would be bad, mmmkay.

As you can see, using >0 in the boolean expression is not as tidy as using an enum.
« Last Edit: May 15, 2013, 10:33:41 PM by CADbloke »

TheMaster

  • Guest
Re: Bitwise setting storage
« Reply #11 on: May 15, 2013, 08:26:15 PM »
What does George Boole have to do with whether or not an undefined enum can exist?

Oh not terribly much :roll:, He's just the guy that came up with the entire concept of using a single numeric integral value to represent multiple true/false states, which is what an Enum with the [Flags] attributes represents.

Quote

Code - C#: [Select]
  1. //
  2. //there are no enum values in the definition that equate to 0,3,5,7,9,11,13 or 15
  3. //they are achieved through bitwise operations
  4. [Flags]
  5. public enum MyEnum
  6. {
  7.      read = 1,
  8.      write = 2,
  9.      create = 4,
  10.      delete = 8
  11. }
  12.  
  13.  

Let me try this:

Are enums essentially integers that we can assign any arbitrary but valid value to, even if it isn't defined at designtime? Or are we basically saying when we declare enums that *thesevariables* equal *thesevalues* and MyEnum someVal = 15 is just as valid as (if not more appropriate than) this:

Code - C#: [Select]
  1. //
  2. //rwcd = 15 is redundant and unnecessary
  3. //
  4. [Flags]
  5. public enum MyEnum
  6. {
  7.      read = 1,
  8.      write = 2,
  9.      create = 4,
  10.      delete = 8,
  11.      rwcd = 15
  12. }
  13.  
  14.  


The problem is that you're expressing the logical 'or' of multiple Enum fields as a literal integer, which is technically-considered a no-no (see below).

As far as the 'rwcd' enum member, yes you can define that if you want, as a convenience that avoids having to logically-combine the component fields using the | (or) operator, and as a way of ensuring that existing code is not broken as it would be if you were to express the logical combination of several members as a literal integer (see below).

Here is an example from my post showing how to use the MatchProperties protocol extension, that also defines a 'synonym' (MathPropFlags.Entity) that is the logical combination of several other members. Notice that the Entity member's value is not expressed as a literal integer, but rather by using the logical 'or' (|) operator to logically-combine the members it acts as a synonym for, which is important:

Code - C#: [Select]
  1. [Flags]
  2.    public enum MatchPropFlags
  3.    {
  4.       Color = 0x1,
  5.       Layer = 0x2,
  6.       Linetype = 0x4,
  7.       Thickness = 0x8,
  8.       LinetypeScale = 0x10,
  9.       Text = 0x20,
  10.       Dimension = 0x40,
  11.       Hatching = 0x80,
  12.       Lineweight = 0x100,
  13.       PlotStyleName = 0x200,
  14.       Polyline = 0x400,
  15.       Viewport = 0x800,
  16.       Table = 0x1000,
  17.       Material = 0x2000,
  18.       ShadowDisplay = 0x4000,
  19.       Multileader = 0x8000,
  20.       All = 0xFFFF,
  21.       Entity = Color | Layer | Linetype | Thickness
  22.          | LinetypeScale | Material | Lineweight
  23.          | PlotStyleName | ShadowDisplay
  24.    };
  25.  
  26.  

Notice that the last element is actually a logical combination of all of the elements shown between the | (or) operators, and that serves two purposes. It is a convenience to avoid having to express the combined elements literally each time that value is used, and it also insulates the code that uses the 'Entity' member from future changes that may occur (a perfect example being that in a later AutoCAD release, the Transparency flag was added to the Entity member, so any existing code that runs on supported releases will automatically support matching an entity's Transparency property, if it used the 'Entity' member, verses explicitly specifying all of its components individually).

In code you wouldn't express it as an integer because that elminates one of the main advantages that using an enum has, which is that you can change its definition without having to find every (incorrect) use of an integer that expresses multiple bits.

In other words, instead of saying:

Code - C#: [Select]
  1. [Flags]
  2. public enum MyEnum
  3. {
  4.      read = 1,
  5.      write = 2,
  6.      create = 4,
  7.      delete = 8,
  8.      rwcd = 15       <<< this is a no-no
  9. }
  10.  

You are supposed to express it thusly in code:

Code - C#: [Select]
  1. [Flags]
  2. public enum MyEnum
  3. {
  4.      read = 1,
  5.      write = 2,
  6.      create = 4,
  7.      delete = 8,
  8.      rwcd = read | write | create | delete
  9. }
  10.  

So, the bitwise '|' ("Or") operator logically-combines the values of two or more members of the enum in a way that does not create a dependence on their exact values, which would allow you to change the definition of the Enum after the fact, without breaking any existing code that uses the enum members (as opposed to using integers).

Similarly, you should never express the logical-combination of multiple enum members literally as an integer anywhere in code, because that creates a dependence on the actual value at the time the code was written, and would be broken if you were to modify the enum's definition after the fact.

Yes, an enum is just compiler sugar for an ordinal type (it could be a short, integer, or long, which you can define yourself using the : operator). In the compiled code it ends up as a number.

Quote

Incidently, I have finished coding the class in which this is used. I ended up with 17 different enums (I was able to reduce it from 23) and 174 values in those 17 enums. All told there are over 200 permissions that this will be used to manage.

Great. The most interesting part is in how you connect enum values to UI elements like check boxes (using Data binding and custom type descriptors). 

I don't have a shareable example of that handy, but in short, I can data bind the Checked property of a checkbox to a property of an Enum type, and specify the Enum member that the checkbox controls, without having to write any code.
« Last Edit: May 15, 2013, 08:39:52 PM by TT »

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
Re: Bitwise setting storage
« Reply #12 on: May 15, 2013, 10:51:01 PM »
I'm not defining any overlapping values in the enums, just the values needed for the various permissions groups. Also, because the class doesn't allow properties to be set except through the constructor, it should prevent a user from elevating their own permissions .. or so I am told.

The way this is supposed to work is only a top level admin can define a user and their permissions. The aggregation of the enum values will be stored in various database fields as integers. When a user logs in, their user object will be instantiated with their permissions, then depending upon their permissions, other things will be available for their use. For example:

Code - C#: [Select]
  1. //Show-Hide button to edit NCIC documents
  2. Button btnDeleteNCICDocument.Visible = _user.CanDeleteNCICDocument();
  3. //Enable-Disable textbox to allow username field to be edited
  4. Textbox txtUserName.Enabled = _user.CanEditUsername();
  5. //Show-Hide textbox with SSN
  6. Textbox txtSocialSecurityNumber.Visible = _user.CanViewSSN();
  7. //Enable-Disable textbox with SSN
  8. Textbox txtSocialSecurityNumber.Enabled = _user.CanEditSSN();
  9.  

... which begs the question; because the values are stored as integers in the database, I have cast them to the enum types in the constructor ... is this a necessary (or advisable) step. I know code can be pretty forgiving when it comes to implicit casting, but I don't want any surprises.

Code - C#: [Select]
  1. public User(int32 documentPermissions, int32 personelPermissions, int32 administrativePermissions, ... )
  2. {
  3.     DocumentPermission _docPerms = (DocumentPermission)documentPermissions;
  4.     PersonelPermission _empPerms = (PersonelPermission)personelPermissions;
  5.     AdminPermission _adminPerms = (AdminPermission)administrativePermissions;
  6.     ...
  7. }
  8.  

In the user management console (which is yet to be designed) there will be many checkboxes, radiobuttons and comboboxes which will likely be databound to the table of users pulled from the database. This will allow the admin to quickly navigate and edit user permissions as needed.

I am open for suggestions on how to make that process as simple as possible as well, because quite honestly, this application is going to be huge and I need to design it to be as efficient as I am capable of doing ... and then more efficient than that.

I don't know if I mentioned it elsewhere (I didn't here), but this is a web application with lots of data being managed, so I am having to be particularly careful about postbacks when the user interacts with the page. I'll be doing much of the application in javascript so it can reduce some of the transactions.

While we're on the subject of ANDs and ORs, make sure you are clear on the difference between logical and boolean operators and also that || "short-circuits" an evaluation and may be quicker. See http://stackoverflow.com/q/1279217/492 and the linked questions for clarity.

Yeah, I know the difference, I inadvertently used the logical comparison in my original post. I am/was doing bitwise math, not logical comparison, at least not in the context of the && used in my first post.

Incidently, I started this class using constants, and had it about half completed, but after looking through the information about enums and flags, I completely recoded the class to use enums and finished it in less time than it took to code the first half using constants and bitwise math, so yeah, I think I am a convert to enums as flags now.
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Bitwise setting storage
« Reply #13 on: May 19, 2013, 05:13:51 AM »
Great. The most interesting part is in how you connect enum values to UI elements like check boxes (using Data binding and custom type descriptors). 

I don't have a shareable example of that handy, but in short, I can data bind the Checked property of a checkbox to a property of an Enum type, and specify the Enum member that the checkbox controls, without having to write any code.

Hi Tony,

I'm really interested by the way you do this.
By my side, I set each CheckBox.Tag to an Enum value and use a single handler for the CheckedChanged event, but I'm certain you use a more efficient way.

Code - C#: [Select]
  1. using System;
  2. using System.Linq;
  3. using System.Windows.Forms;
  4.  
  5. namespace FlagSample
  6. {
  7.     [Flags]
  8.     public enum Bits
  9.     {
  10.         None = 0,
  11.         Bit0 = 1,
  12.         Bit1 = 2,
  13.         Bit2 = 4,
  14.         Bit3 = 8,
  15.     }
  16.  
  17.     public partial class Form1 : Form
  18.     {
  19.         private Bits flags;
  20.         private CheckBox[] chkBoxes;
  21.  
  22.         public Form1()
  23.         {
  24.             InitializeComponent();
  25.             this.chkBoxes = new CheckBox[4]
  26.             { checkBox1, checkBox2, checkBox3, checkBox4 };
  27.             Array values = Enum.GetValues(typeof(Bits));
  28.             for (int i = 0; i < this.chkBoxes.Length; i++)
  29.             {
  30.                 CheckBox chk = this.chkBoxes[i];
  31.                 chk.Tag = values.GetValue(i + 1);
  32.                 chk.CheckedChanged += OnCheckedChanged;
  33.             }
  34.         }
  35.  
  36.         public Bits Flags
  37.         {
  38.             get { return flags; }
  39.             set
  40.             {
  41.                 flags = value;
  42.                 foreach (CheckBox chk in chkBoxes)
  43.                 {
  44.                     chk.Checked = flags.HasFlag((Bits)chk.Tag);
  45.                 }
  46.             }
  47.         }
  48.  
  49.         private void OnCheckedChanged(object sender, EventArgs e)
  50.         {
  51.             CheckBox chk = (CheckBox)sender;
  52.             if (chk.Checked)
  53.                 this.flags |= (Bits)chk.Tag;
  54.             else
  55.                 this.flags &= ~(Bits)chk.Tag;
  56.         }
  57.  
  58.         // ...
  59.     }
« Last Edit: May 19, 2013, 07:40:22 AM by gile »
Speaking English as a French Frog

kaefer

  • Guest
Re: Bitwise setting storage
« Reply #14 on: May 19, 2013, 08:11:00 AM »
Code - C#: [Select]
  1. [Flags]
  2. public enum MyEnum
  3. {
  4.      read = 1,
  5.      write = 2,
  6.      create = 4,
  7.      delete = 8,
  8.      rwcd = read | write | create | delete
  9. }
  10.  

This kind of defeats the purpose of what an enum with the FlagsAttribute represents, doesn't it?

Its ToString() method wouldn't return the string "read, write, create, delete" but "rwcd" instead, when encountering the combined value. If I'm interpreting the doc correctly, this is all that the FlagsAttribute does.

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
Re: Bitwise setting storage
« Reply #15 on: May 19, 2013, 09:13:45 AM »
I read some other documentation that actually encouraged overlapping bits as a way of making the code more understandable, but not with the abbreviated form I used in the original bit of code.

As far as I am concerned, creating an enum value for flag combinations is not only inefficient from the coding point of view, but negates the reason to use flags in the first place.
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

TheMaster

  • Guest
Re: Bitwise setting storage
« Reply #16 on: May 20, 2013, 02:59:22 AM »
Code - C#: [Select]
  1. [Flags]
  2. public enum MyEnum
  3. {
  4.      read = 1,
  5.      write = 2,
  6.      create = 4,
  7.      delete = 8,
  8.      rwcd = read | write | create | delete
  9. }
  10.  

This kind of defeats the purpose of what an enum with the FlagsAttribute represents, doesn't it?

Its ToString() method wouldn't return the string "read, write, create, delete" but "rwcd" instead, when encountering the combined value. If I'm interpreting the doc correctly, this is all that the FlagsAttribute does.


Did you read the doc you refer to above?

Specifically, under "Guidelines for FlagsAttribute and Enum", the third bulleted item:

Quote
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.

Beyond that, an enumerated constant can be the logical union of several other constants, but can still have a different meaning beyond that, as is the case in the MatchPropFlags enum I posted above.

Here it is again, with the revision that I had to make when entity Transparency was introduced:

This was the enum before the Entity.Transparency property was introduced:

Code - C#: [Select]
  1.  
  2.    [Flags]
  3.    public enum MatchPropFlags
  4.    {
  5.       Color = 0x1,
  6.       Layer = 0x2,
  7.       Linetype = 0x4,
  8.       Thickness = 0x8,
  9.       LinetypeScale = 0x10,
  10.       Text = 0x20,
  11.       Dimension = 0x40,
  12.       Hatching = 0x80,
  13.       Lineweight = 0x100,
  14.       PlotStyleName = 0x200,
  15.       Polyline = 0x400,
  16.       Viewport = 0x800,
  17.       Table = 0x1000,
  18.       Material = 0x2000,
  19.       ShadowDisplay = 0x4000,
  20.       Multileader = 0x8000,
  21.       All = 0xFFFF,
  22.       Entity = Color | Layer | Linetype | Thickness
  23.          | LinetypeScale | Material | Lineweight
  24.          | PlotStyleName | ShadowDisplay
  25.    };
  26.  
  27.  

When the Entity.Transparency property was introduced, the Transparency bit flag was added, and the Entity constant was modified to include it:

Code - C#: [Select]
  1.    
  2.   Transparency = 0x10000,  
  3.  
  4.   Entity = Color | Layer | Linetype | Thickness
  5.      | LinetypeScale | Material | Lineweight
  6.      | PlotStyleName | ShadowDisplay | Transparency
  7.  
  8.  

The point to the Entity constant (which like the rwcd constant in the example you quoted), is to specify that all Entity properties are to be matched, in a non-literal way. IOW, it means all entitiy properties, including any that are added in the future, which is not exactly the same as 'all entity properties that existed when the enum was defined or used'.

So, when the Transparency flag was added to support entity transparency in MATCHPROPS, all existing code that used the Entity member, rather than explicitly specifying each of the flags it represents, automatically supported matching entity Transparency without having to be revised.

If in my code where this enum was used, I instead used the individual flags when the intent was to match all entity properties, then I would have had to revise each place where those flags were used, and add the Transparency flag.

So, in this case the Entity member has its own meaning, in spite of the fact that its value is the logical or of all the flags for each Entity's property. Perhaps its meaning would be clearer if I had given it the name "AllSupportedEntityProperties" ?

In the case of the example you quoted, it could have the meaning "full permissions", which could make its meaning slightly different then read+write+create+delete, as well.

The FlagsAttribute itself doesn't really do anything, but it is recognized in a number of places in the framework. For example, the PropertyGrid will behave differently when that attribute is applied to a property of an Enum type. There are also cases where data binding might specifically look for the flag to bind to a specific field, rather than the aggregated value.

 
[I revised this post after having read the referenced docs]
« Last Edit: May 20, 2013, 06:42:59 AM by TT »

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Bitwise setting storage
« Reply #17 on: May 20, 2013, 12:39:22 PM »
In case any of you guys did not know if you have enumeration you deal with a lot you can use extension methods to make the enumeration act as though it has methods.
 
Simple example and sure you guys could come up with some useful ones for your needs.
Code - C#: [Select]
  1.  
  2.    [Flags]
  3.     public enum Settings
  4.     {
  5.         CanCopy = 1,
  6.         CanOpen = 2,
  7.         CanEdit = 4,
  8.         CanDelete = 8
  9.     }
  10.     public static class SettingsExtension
  11.     {
  12.         public static Settings SetFlag(this Settings set, Settings newFlag)
  13.         {
  14.             return set | newFlag;
  15.         }
  16.         public static Settings ClearFlag(this Settings set, Settings clearFlag)
  17.         {
  18.             return set & ~clearFlag;
  19.         }
  20.     }

 

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
Re: Bitwise setting storage
« Reply #18 on: May 20, 2013, 02:43:27 PM »
whoa .. that is cool ... I'll have to remember that!
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

kaefer

  • Guest
Re: Bitwise setting storage
« Reply #19 on: May 20, 2013, 02:46:07 PM »
Did you read the doc you refer to above?

I wish I had read the part about the FlagsAttribute being a mere convention to mark enumerations that can be treated as bit fields. Apart from the inconsistency of treating certain bit combinations specially, I should be agnostic to what the third bulleted item suggests. The surprise was in the paucity of actual functionality of the FlagsAtrribute.

<example of genial enum encapsulated version dependency omitted, which does not rely on the presence of the FlagsAttribute>

Quote
The FlagsAttribute itself doesn't really do anything, but it is recognized in a number of places in the framework. For example, the PropertyGrid will behave differently when that attribute is applied to a property of an Enum type. There are also cases where data binding might specifically look for the flag to bind to a specific field, rather than the aggregated value.

This latter sounds rather interesting, and perhaps pertinent, considering the need to bind bitwise-coded settings to an UI.

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
Re: Bitwise setting storage
« Reply #20 on: May 20, 2013, 02:58:34 PM »
At least in my application, I won't be binding settings to controls because the settings will be read only or not accessible for the current user. For the super-admin, who has access to edit user settings, changing the settings will be done one by one.

... although, binding could present a simpler option to managing user settings ... is it possible to bind multiple controls to a single enum variable?
For example, in our documents permission group, there are 8 permissions. The DocPerm database value can be any value from 0 to 255 representing any combination of settings. All permission controls are checkboxes .. so CheckBox1.checked = HasThisPermission(); or is there a binding that can be set? Then I can simply update the database with the bound data.
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

CADbloke

  • Bull Frog
  • Posts: 342
  • Crash Test Dummy
Re: Bitwise setting storage
« Reply #21 on: May 21, 2013, 10:28:46 PM »
A good discussion on Stack Overflow clarifying the Enum Flags Attribute -  http://stackoverflow.com/questions/8447/enum-flags-attribute.

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
Re: Bitwise setting storage
« Reply #22 on: May 21, 2013, 10:56:38 PM »
I read that thread before embarking on this journey. It is a good explanation.

There is one thing that I did discover. The HasFlag method wasn't introduced until .Net4.0

Initially this project was supposed to be .Net3.5. Due to the shared nature of the deployment server the hosting company wouldn't install the latest versions of .Net just in case it might affect someone elses website. Apparently they don't understand the nature of the .net libraries and the fact that the versions are targeted. Anyway, we have since went from shared hosting to owning a webserver outright. I am free to develop with the latest libraries ... makes life easier ;-)
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Bitwise setting storage
« Reply #23 on: May 22, 2013, 12:15:38 AM »
Hi Keith,
 
You mentioned this needing to be efficient and not sure how often and how many calls you will make so might not be worth it, but if you look at HasFlag signature it takes a Enum as its parameter.
Code - C#: [Select]
  1.         [SecuritySafeCritical, __DynamicallyInvokable]
  2.         public bool HasFlag(Enum flag)
  3.         {
  4.             if (flag == 0)
  5.             {
  6.                 throw new ArgumentNullException("flag");
  7.             }
  8.             if (!base.GetType().IsEquivalentTo(flag.GetType()))
  9.             {
  10.                 throw new ArgumentException(Environment.GetResourceString("Argument_EnumTypeDoesNotMatch", new object[] { flag.GetType(), base.GetType() }));
  11.             }
  12.             return this.InternalHasFlag(flag);
  13.         }
  14.  
  15.  

 
So any value you pass in will get boxed.
 
Might help to use a extension method to save some cycles.
Maybe something similar as last method below IsSet.
Code - C#: [Select]
  1.  
  2.     [Flags]
  3.     public enum Settings
  4.     {
  5.         CanCopy = 1,
  6.         CanOpen = 2,
  7.         CanEdit = 4,
  8.         CanDelete = 8
  9.     }
  10.     class Program
  11.     {
  12.         static void Main(string[] args)
  13.         {
  14.             Settings settings = Settings.CanCopy;
  15.             settings = settings.SetFlag(Settings.CanDelete);
  16.             Console.WriteLine(settings.IsSet(Settings.CanCopy));
  17.             Console.WriteLine(settings.IsSet(Settings.CanDelete));
  18.             Console.WriteLine(settings.IsSet(Settings.CanEdit));
  19.             Console.Read();
  20.         }
  21.     }
  22.     public static class SettingsExtension
  23.     {
  24.         public static Settings SetFlag(this Settings set, Settings newFlag)
  25.         {
  26.             return set | newFlag;
  27.         }
  28.         public static Settings ClearFlag(this Settings set, Settings clearFlag)
  29.         {
  30.             return set & ~clearFlag;
  31.         }
  32.         public static bool IsSet(this Settings set, Settings testFlag)
  33.         {
  34.             if (testFlag == 0)
  35.             {
  36.                 throw new ArgumentNullException("testFlag");
  37.             }
  38.             return (set & testFlag) == testFlag;
  39.         }
  40.     }
  41.  
  42.  

 

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
Re: Bitwise setting storage
« Reply #24 on: May 22, 2013, 06:57:43 AM »
This is a web application, so I am not sure that any method is going to be super efficient, but as it stands right now I've used the HasFlag method to verify the permissions.

When a user logs into the website, their credentials are verified against those stored in the database. If they are successful during login, a user object is created. Of course, this being the web, the user object isn't persistent, so ... whenever the user browses to a different page, their credentials are verified against those in the database and the page is rendered according to their permissions.

The user object is not recreated on postback unless it is needed.

The inefficiencies are introduced because before any page can be rendered, every possible permission has to be evaluated so the proper controls can be created. When you have over 200 permissions, it gets a bit hairy, although, by grouping the permissions, I can short circuit alot of the checks, because for example, if the user doesn't have the "CanSeeAdminTab" property set, we don't need to check the 50 or so admin permissions because the admin tab will never be rendered anyway. The same thing happens with the other 16 permission groups.

I am sure there could be some tweaks that would make the process more efficient, but right now, the goal is to get a working system to verify user credentials.
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

TheMaster

  • Guest
Re: Bitwise setting storage
« Reply #25 on: May 22, 2013, 04:46:02 PM »
Hi Keith,
 
You mentioned this needing to be efficient and not sure how often and how many calls you will make so might not be worth it, but if you look at HasFlag signature it takes a Enum as its parameter.
Code - C#: [Select]
  1.         [SecuritySafeCritical, __DynamicallyInvokable]
  2.         public bool HasFlag(Enum flag)
  3.         {
  4.             if (flag == 0)
  5.             {
  6.                 throw new ArgumentNullException("flag");
  7.             }
  8.             if (!base.GetType().IsEquivalentTo(flag.GetType()))
  9.             {
  10.                 throw new ArgumentException(Environment.GetResourceString("Argument_EnumTypeDoesNotMatch", new object[] { flag.GetType(), base.GetType() }));
  11.             }
  12.             return this.InternalHasFlag(flag);
  13.         }
  14.  
  15.  

 
So any value you pass in will get boxed.
 

The initial implementation of HasFlag() had major performance issues that were reported on connect and written about elsewhere, that were addressed in .NET 4.0

Code - C#: [Select]
  1. public bool HasFlag(Enum flag)
  2. {
  3.    if (!base.GetType().IsEquivalentTo(flag.GetType()))
  4.    {
  5.       throw new ArgumentException(Environment.GetResourceString("Argument_EnumTypeDoesNotMatch", new object[] { flag.GetType(), base.GetType() }));
  6.    }
  7.    ulong num = ToUInt64(flag.GetValue());
  8.    return ((ToUInt64(this.GetValue()) & num) == num);
  9. }
  10.  
  11.  

Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
Re: Bitwise setting storage
« Reply #26 on: May 22, 2013, 05:25:49 PM »
So, what is the prognosis? Is using HasFlag the best alternative? I do have to process 200+ permissions on each page render that is not a postback.
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

TheMaster

  • Guest
Re: Bitwise setting storage
« Reply #27 on: May 22, 2013, 05:39:52 PM »
So, what is the prognosis? Is using HasFlag the best alternative? I do have to process 200+ permissions on each page render that is not a postback.

Jeff's point about boxing is still an issue (an argument of type Enum will be boxed), and HasFlag() is much slower than using the 'flag & bits == bits' test, but this does not become a major issue unless you are calling it at a very high frequency. 

Most programmers will opt for HasFlags() simply because it is less typing, which is fine as long as its not being called at a very high frequency.


Keith™

  • Villiage Idiot
  • Seagull
  • Posts: 16899
  • Superior Stupidity at its best
Re: Bitwise setting storage
« Reply #28 on: May 22, 2013, 05:52:57 PM »
Well, as I stated earlier, because this is a webpage, I can't persist the user object and I have to validate on each new page view. The good thing is that I don't have to validate on postback and there are only a dozen or so pages that are viewed. For the most part, the user will remain on the same page for the entire session until they log out.
Proud provider of opinion and arrogance since November 22, 2003 at 09:35:31 am
CadJockey Militia Field Marshal

Find me on https://parler.com @kblackie

CADbloke

  • Bull Frog
  • Posts: 342
  • Crash Test Dummy
Re: Bitwise setting storage
« Reply #29 on: May 22, 2013, 06:37:33 PM »
For the most part, the user will remain on the same page for the entire session until they log out.

Have you looked into SignalR and/or Single Page Applications?

Also, the ASP.NET [Authorize] Attribute may be helpful? See also http://stackoverflow.com/q/970271/492 and related questions.