What to Do When Bit Mask (Flags) Enum Gets Too Large

What to do when bit mask (flags) enum gets too large

I see values from at least a handful of different enumerations in there...

My first thought was to approach the problem by splitting the permissions up in logical groups (RuleGroupPermissions, RulePermissions, LocationPermissions, ...), and then having a class (WebAgentPermissions) exposing a property for each permission enum type.

Since the permission values seem repetitive, you could probably get away with a single enum in the end:

[Flags]
public enum Permissions
{
View = 1,
Add = 2,
Edit = 4,
Delete = 8
}

And then have the WebAgentPermissions class expose a property for each area where permissions are to be set;

class WebAgentPermissions
{
public Permissions RuleGroup { get; set; }
public Permissions Rule { get; set; }
public Permissions Location { get; set; }
// and so on...
}

Flags enum with too many items; last value is too large. How can I resolve this?

You can specify the type of the enum to be something with a greater range.

[Flags]
public enum Types : long

Large flags enumerations in C#

You could write a T4 template to generate the enum :

Template (MyEnumeration.tt)

<#@ template language="C#" #>
<#@ output extension=".cs" #>
using System;

namespace MyNamespace
{
[Flags]
public enum MyEnumeration : ulong
{
<#
ulong value = 1;
for(int i = 1; i <= 64; i++)
{
#>
Flag<#= i #> = <#= string.Format("0x{0:X8}", value) #>,
<#
value = value << 1;
}
#>
}
}

Resulting C# code (MyEnumeration.cs)

using System;

namespace MyNamespace
{
[Flags]
public enum MyEnumeration : ulong
{
Flag1 = 0x00000001,
Flag2 = 0x00000002,
Flag3 = 0x00000004,
Flag4 = 0x00000008,
Flag5 = 0x00000010,
Flag6 = 0x00000020,
Flag7 = 0x00000040,
Flag8 = 0x00000080,
Flag9 = 0x00000100,
Flag10 = 0x00000200,
Flag11 = 0x00000400,
Flag12 = 0x00000800,
Flag13 = 0x00001000,
Flag14 = 0x00002000,
Flag15 = 0x00004000,
Flag16 = 0x00008000,
Flag17 = 0x00010000,
Flag18 = 0x00020000,
Flag19 = 0x00040000,
Flag20 = 0x00080000,
Flag21 = 0x00100000,
Flag22 = 0x00200000,
Flag23 = 0x00400000,
Flag24 = 0x00800000,
Flag25 = 0x01000000,
Flag26 = 0x02000000,
Flag27 = 0x04000000,
Flag28 = 0x08000000,
Flag29 = 0x10000000,
Flag30 = 0x20000000,
Flag31 = 0x40000000,
Flag32 = 0x80000000,
Flag33 = 0x100000000,
Flag34 = 0x200000000,
Flag35 = 0x400000000,
Flag36 = 0x800000000,
Flag37 = 0x1000000000,
Flag38 = 0x2000000000,
Flag39 = 0x4000000000,
Flag40 = 0x8000000000,
Flag41 = 0x10000000000,
Flag42 = 0x20000000000,
Flag43 = 0x40000000000,
Flag44 = 0x80000000000,
Flag45 = 0x100000000000,
Flag46 = 0x200000000000,
Flag47 = 0x400000000000,
Flag48 = 0x800000000000,
Flag49 = 0x1000000000000,
Flag50 = 0x2000000000000,
Flag51 = 0x4000000000000,
Flag52 = 0x8000000000000,
Flag53 = 0x10000000000000,
Flag54 = 0x20000000000000,
Flag55 = 0x40000000000000,
Flag56 = 0x80000000000000,
Flag57 = 0x100000000000000,
Flag58 = 0x200000000000000,
Flag59 = 0x400000000000000,
Flag60 = 0x800000000000000,
Flag61 = 0x1000000000000000,
Flag62 = 0x2000000000000000,
Flag63 = 0x4000000000000000,
Flag64 = 0x8000000000000000,
}
}

In order to edit T4 templates, I recommend you use a T4 editor plugin like this one (this gives you syntax highlighting and Intellisense)

Is there a Python class/enum for flag/bit mask operations?

I've recently published an opensource package py-flags that aims this problem. That library has exactly this functionality and its design is heavily influenced by the python3 enum module.

There are debates about whether it is pythonic enough to implement such a flags class because its functionality has huge overlaps with other methods provided by the language (collection of bool variables, sets, objects with bool attributes or dicts with bool items, ...). For this reason I feel a flags class to be too narrow purpose and/or redundant to make its way to the standard library but in some cases it is much better than the previously listed solutions so having a "pip install"-able library can come in handy.

Your example would look like the following using the py-flags module:

from flags import Flags

class NetlistKind(Flags):
Unknown = 0
LatticeNetlist = 1
QuartusNetlist = 2
XSTNetlist = 4
CoreGenNetlist = 8
All = 15

The above things could be tweaked a bit further because a flags class declared with the library automatically provides two "virtual" flags: NetlistKind.no_flags and NetlistKind.all_flags. These make the already declared NetlistKind.Unknown and NetlistKind.All redundant so we could leave them out from the declaration but the problem is that no_flags and all_flags don't match your naming convention. To aid this we declare a flags base class in your project instead of flags.Flags and you will have to use that in the rest of your project:

from flags import Flags

class BaseFlags(Flags):
__no_flags_name__ = 'Unknown'
__all_flags_name__ = 'All'

Based on the previously declared base class that can be subclassed by any of your flags in your project we could change your flag declaration to:

class NetlistKind(BaseFlags):
LatticeNetlist = 1
QuartusNetlist = 2
XSTNetlist = 4
CoreGenNetlist = 8

This way NetlistKind.Unknown is automatically declared with a value of zero. NetlistKind.All is also there and it is automatically the combination of all of your declared flags. It is possible to iterate enum members with/without these virtual flags. You can also declare aliases (flags that have the same value as another previously declared flag).

As an alternative declaration using the "function-call style" (also provided by the standard enum module):

NetlistKind = BaseFlags('NetlistKind', ['LatticeNetlist', 'QuartusNetlist',
'XSTNetlist', 'CoreGenNetlist'])

If a flags class declares some members then it is considered to be final. Trying to subclass it will result in error. It is semantically undesired to allow subclassing a flag class for the purpose of adding new members or change functionality.

Besides this the flags class provides the operators your listed (bool operators, in, iteration, etc...) in a type-safe way. I'm going to finish the README.rst along with a little plumbing on the package interface in the next few days but the basic functionality is already there and tested with quite good coverage.

Switch on Enum (with Flags attribute) without declaring every possible combination?

How about this. Of course the arguments and return types of DoSomething, etc., can be anything you like.

class Program
{
[Flags]
public enum CheckType
{
Form = 1,
QueryString = 2,
TempData = 4,
}

private static bool DoSomething(IEnumerable cln)
{
Console.WriteLine("DoSomething");
return true;
}

private static bool DoSomethingElse(IEnumerable cln)
{
Console.WriteLine("DoSomethingElse");
return true;
}

private static bool DoWhatever(IEnumerable cln)
{
Console.WriteLine("DoWhatever");
return true;
}

static void Main(string[] args)
{
var theCheckType = CheckType.QueryString | CheckType.TempData;
var checkTypeValues = Enum.GetValues(typeof(CheckType));
foreach (CheckType value in checkTypeValues)
{
if ((theCheckType & value) == value)
{
switch (value)
{
case CheckType.Form:
DoSomething(null);
break;
case CheckType.QueryString:
DoSomethingElse(null);
break;
case CheckType.TempData:
DoWhatever(null);
break;
}
}
}
}
}

Flags enum & bitwise operations vs. “string of bits”

Benefits of using Flags enum:

  • Standard approach: "They are the correct design to use when multiple enumeration values can be specified at the same time."
  • Intent is clear
  • Maintainable -- new programmers should pick this up easily
  • Easily extensible -- support for new flag combinations (e.g. weekend)
  • Fast

Negatives of using Flags enum:

  • Data representation for humans hard to understand (e.g. what flags are set for 17?)


Benefits of using string of bits:

  • Easy for programmers to see which bits are set in string

Negatives of using string of bits:

  • Non-standard approach
  • Harder to understand for programmers unfamiliar with your design
  • Potentially easier to set "garbage" values (e.g. stringValue = "Sunday")
  • Needless string creation
  • Needless string parsing
  • Additional development work
  • Reinventing the wheel (but not even a round wheel)


How important is it really to be able to look at the string of bits to see what is set? If it's hard to know that 17 is Monday and Friday, you can always use calculator and convert to binary. Or add some sort of string representation for "display" (or debugging) use. It's not that difficult.


It also seems to me that if you are going to make the string of bits approach solid then you will need to do quite a bit of encapsulation to bring it up to a level of abstraction that the Flags enum already provides. If the approach is to simply manipulate the string of bits directly then that is going to be hard to read (and understand) and probably error prone.

e.g. you may end up seeing this:

days = "1000101"; // fixed bug where days were incorrectly set to "1010001"

Combining Enum Values with Bit-Flags

Enum.HasFlag is what you want to use

Console.WriteLine("Custodian is in All: {0}", Role.All.HasFlag(Role.Custodian));

Just noticed that your enum should be defined like this with the Flags attribute and values spaced out by powers of 2

[Flags]
public enum Role
{
NormalUser = 1,
Custodian = 2,
Finance = 4,
SuperUser = Custodian | Finance,
All = Custodian | Finance | NormalUser
}

The reason powers of 2 are used for flagged enums is that each power of 2 represents a unique bit being set in the binary representation:

NormalUser = 1 = 00000001
Custodian = 2 = 00000010
Finance = 4 = 00000100
Other = 8 = 00001000

Because each item in the enum has a unique bit set this allows them to be combined by setting their respective bits.

SuperUser  = 6 = 00000110 = Custodian + Finance
All = 7 = 00000111 = NormalUser + Custodian + Finance
NormOther = 9 = 00001001 = NormalUser + Other

Notice how each 1 in the binary form lines up with the bit set for the flag in the section above.

When you use flag(Enum) you have a limit of 64. What are the alternative when you reach the limit?

You could then switch to using a BitArray. You would lose all the "features" of an enum such as the default string formatting and parsing ability. A BitArray would be fundamentally similar to having a bunch of boolean fields except that the storage is much more efficient.

Indeed as Jeff says in the comments with that many independent bit states it seems like an Enum is the wrong solution anyway. A BitArray may be much more suitable for your particular scenario.



Related Topics



Leave a reply



Submit