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

C#PermissionsEnumsTypesBitmask

C# Problem Overview


I have a very large set of permissions in my application that I represent with a Flags enumeration. It is quickly approaching the practical upper bound of the long data type. And I am forced to come up with a strategy to transition to a different structure soon. Now, I could break this list down into smaller pieces, however, this is already just a subset of the overall permissions for our application, based on our applications layout. We use this distinction extensively for display purposes when managing permissions and I would rather not have to revisit that code at this time if I can avoid it.

Has anybody else run into this issue? How did you get past it? General examples are fine, but I am most interested in a c# specific example if there are any language specific tricks that I can employ to get the job done.

May not be neccessary, but here is the list of Permissions currently defined for the portion of the app I am dealing with.

//Subgroup WebAgent
[Flags]
public enum WebAgentPermission : long
{
    [DescriptionAttribute("View Rule Group")]
    ViewRuleGroup = 1,
    [DescriptionAttribute("Add Rule Group")]
    AddRuleGroup = 2,
    [DescriptionAttribute("Edit Rule Group")]
    EditRuleGroup = 4,
    [DescriptionAttribute("Delete Rule Group")]
    DeleteRuleGroup = 8,
    [DescriptionAttribute("View Rule")]
    ViewRule = 16,
    [DescriptionAttribute("Add Rule")]
    AddRule = 32,
    [DescriptionAttribute("Edit Rule")]
    EditRule = 64,
    [DescriptionAttribute("Delete Rule")]
    DeleteRule = 128,
    [DescriptionAttribute("View Location")]
    ViewLocation = 256,
    [DescriptionAttribute("Add Location")]
    AddLocation = 512,
    [DescriptionAttribute("Edit Location")]
    EditLocation = 1024,
    [DescriptionAttribute("Delete Location")]
    DeleteLocation = 2048,
    [DescriptionAttribute("View Volume Statistics")]
    ViewVolumeStatistics = 4096,
    [DescriptionAttribute("Edit Volume Statistics")]
    EditVolumeStatistics = 8192,
    [DescriptionAttribute("Upload Volume Statistics")]
    UploadVolumeStatistics = 16384,
    [DescriptionAttribute("View Role")]
    ViewRole = 32768,
    [DescriptionAttribute("Add Role")]
    AddRole = 65536,
    [DescriptionAttribute("Edit Role")]
    EditRole = 131072,
    [DescriptionAttribute("Delete Role")]
    DeleteRole = 262144,
    [DescriptionAttribute("View User")]
    ViewUser = 524288,
    [DescriptionAttribute("Add User")]
    AddUser = 1048576,
    [DescriptionAttribute("Edit User")]
    EditUser = 2097152,
    [DescriptionAttribute("Delete User")]
    DeleteUser = 4194304,
    [DescriptionAttribute("Assign Permissions To User")]
    AssignPermissionsToUser = 8388608,
    [DescriptionAttribute("Change User Password")]
    ChangeUserPassword = 16777216,
    [DescriptionAttribute("View Audit Logs")]
    ViewAuditLogs = 33554432,
    [DescriptionAttribute("View Team")]
    ViewTeam = 67108864,
    [DescriptionAttribute("Add Team")]
    AddTeam = 134217728,
    [DescriptionAttribute("Edit Team")]
    EditTeam = 268435456,
    [DescriptionAttribute("Delete Team")]
    DeleteTeam = 536870912,
    [DescriptionAttribute("View Web Agent Reports")]
    ViewWebAgentReports = 1073741824,
    [DescriptionAttribute("View All Locations")]
    ViewAllLocations = 2147483648,
    [DescriptionAttribute("Access to My Search")]
    AccessToMySearch = 4294967296,
    [DescriptionAttribute("Access to Pespective Search")]
    AccessToPespectiveSearch = 8589934592,
    [DescriptionAttribute("Add Pespective Search")]
    AddPespectiveSearch = 17179869184,
    [DescriptionAttribute("Edit Pespective Search")]
    EditPespectiveSearch = 34359738368,
    [DescriptionAttribute("Delete Pespective Search")]
    DeletePespectiveSearch = 68719476736,
    [DescriptionAttribute("Access to Search")]
    AccessToSearch = 137438953472,
    [DescriptionAttribute("View Form Roles")]
    ViewFormRole = 274877906944,
    [DescriptionAttribute("Add / Edit Form Roles")]
    AddFormRole = 549755813888,
    [DescriptionAttribute("Delete UserFormRolesDifferenceMasks")]
    DeleteFormRole = 1099511627776,
    [DescriptionAttribute("Export Locations")]
    ExportLocations = 2199023255552,
    [DescriptionAttribute("Import Locations")]
    ImportLocations = 4398046511104,
    [DescriptionAttribute("Manage Location Levels")]
    ManageLocationLevels = 8796093022208,
    [DescriptionAttribute("View Job Title")]
    ViewJobTitle = 17592186044416,
    [DescriptionAttribute("Add Job Title")]
    AddJobTitle = 35184372088832,
    [DescriptionAttribute("Edit Job Title")]
    EditJobTitle = 70368744177664,
    [DescriptionAttribute("Delete Job Title")]
    DeleteJobTitle = 140737488355328,
    [DescriptionAttribute("View Dictionary Manager")]
    ViewDictionaryManager = 281474976710656,
    [DescriptionAttribute("Add Dictionary Manager")]
    AddDictionaryManager = 562949953421312,
    [DescriptionAttribute("Edit Dictionary Manager")]
    EditDictionaryManager = 1125899906842624,
    [DescriptionAttribute("Delete Dictionary Manager")]
    DeleteDictionaryManager = 2251799813685248,
    [DescriptionAttribute("View Choice Manager")]
    ViewChoiceManager = 4503599627370496,
    [DescriptionAttribute("Add Choice Manager")]
    AddChoiceManager = 9007199254740992,
    [DescriptionAttribute("Edit Chioce Manager")]
    EditChoiceManager = 18014398509481984,
    [DescriptionAttribute("Delete Choice Manager")]
    DeleteChoiceManager = 36028797018963968,
    [DescriptionAttribute("Import Export Choices")] //57
    ImportExportChoices = 72057594037927936
}

C# Solutions


Solution 1 - C#

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

Solution 2 - C#

Language documentation says:

http://msdn.microsoft.com/en-us/library/system.flagsattribute.aspx

"The underlying type is Int32 and so the maximum single bit flag is 1073741824 and obviously there are a total of 32 flags for each enum."

However... UPDATED:

Commenter is correct. Check out this:

http://msdn.microsoft.com/en-us/library/ms182147\(VS.80\).aspx

Int32 is only the DEFAULT datatype! In fact you can specify Int64.

public enum MyEnumType : Int64

...allowing up to 64 values. But that certainly seems to be the maximum, after that you're going to be looking at re-engineering. Without knowing too much about the rest of your solution, I can't say exactly what might suit. But an array (or hash-map) of privilege identifiers is probably the most natural approach.

Solution 3 - C#

You can check BitArray class. Maybe you will use it in future.

Solution 4 - C#

Not an answer to your question, but a related suggestion: we use bitshifting to specify the numeric values, like so:

[Flags]
public enum MyEnumFlags : Int64
{
    None = 0,
    A = 1 << 0,
    B = 1 << 1,
    C = 1 << 2,
    D = 1 << 3,
    E = 1 << 4,
    F = 1 << 5,
    ...etc...

Not so important for the first ten, but after that it gets really handy.

Solution 5 - C#

This turned out to be a more common problem than I thought it would be, where I was representing CSS classes as flags types and there were more than 64 possibilities. I've taken all I learned from that process and turned it into a reusable pattern, albeit since it's a struct, it's a copy-and-paste type pattern.

This is the BigFlags "enumerated type". It uses either BigInteger from System.Numerics, or if there is no way you can reference that assembly, there is a fallback that uses BitArray by simply turning off the NUMERICS preprocessor directive.

It behaves remarkably like a Flags enum, even defining such things as HasFlag(...), GetNames(), GetValues(), TryParse(...), a TypeConverter, IConvertible, etc. Since it does define a TypeConverter and IConvertible, it's also suitable for storing in a data store, albeit always as a string or text data type.

You expose the "enum" values as public static readonly members. Combined enum values are exposed as get-only properties.

To use it, copy and paste the code, then do a search and replace on BigFlags with your struct name, then delete the enums in the TODO section and add your values.

Hope somebody finds it useful.

#define NUMERICS

using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
#if NUMERICS
using System.Numerics;
#endif
using System.Reflection;
using System.Text;
using System.Threading.Tasks;


namespace Aim
{
	/// <summary>
	/// The BigFlags struct behaves like a Flags enumerated type.
	/// <para>
	/// Note that if this struct will be stored in some type of data
	/// store, it should be stored as a string type. There are two
	/// reasons for this:
	/// </para>
	/// <para>
	/// 1. Presumably, this pattern is being used because the number
	/// of values will exceed 64 (max positions in a long flags enum).
	/// Since this is so, there is in any case no numeric type which
	/// can store all the possible combinations of flags.
	/// </para>
	/// <para>
	/// 2. The "enum" values are assigned based on the order that the
	/// static public fields are defined. It is much safer to store
	/// these fields by name in case the fields are rearranged. This
	/// is particularly important if this represents a permission set!
	/// </para>
	/// </summary>
	[
	TypeConverter( typeof( BigFlagsConverter ) )
	]
	public struct BigFlags : IEquatable<BigFlags>,
		IComparable<BigFlags>, IComparable, IConvertible
	{
		#region State...

		private static readonly List<FieldInfo> Fields;
		private static readonly List<BigFlags> FieldValues;
#if NUMERICS
		private static readonly bool ZeroInit = true;
		private BigInteger Value;

		/// <summary>
		/// Creates a value taking ZeroInit into consideration.
		/// </summary>
		/// <param name="index"></param>
		/// <returns></returns>
		private static BigInteger CreateValue( int index )
		{
			if( ZeroInit && index == 0 )
			{
				return 0;
			}
			int idx = ZeroInit ? index - 1 : index;

			return new BigInteger( 1 ) << idx;
		}
#else
		private BitArray Array;

		/// <summary>
		/// Lazy-initialized BitArray.
		/// </summary>
		private BitArray Bits
		{
			get
			{
				if( null == Array )
				{
					Array = new BitArray( Fields.Count );
				}
				return Array;
			}
		}
#endif
		#endregion ...State

		#region Construction...

		/// <summary>
		/// Static constructor. Sets the static public fields.
		/// </summary>
		static BigFlags()
		{
			Fields = typeof( BigFlags ).GetFields(
				BindingFlags.Public | BindingFlags.Static ).ToList();
			FieldValues = new List<BigFlags>();
			for( int i = 0; i < Fields.Count; i++ )
			{
				var field = Fields[i];
				var fieldVal = new BigFlags();
#if NUMERICS
				fieldVal.Value = CreateValue( i );
#else
				fieldVal.Bits.Set( i, true );
#endif
				field.SetValue( null, fieldVal );
				FieldValues.Add( fieldVal );
			}
		}
		#endregion ...Construction

		#region Operators...

		/// <summary>
		/// OR operator. Or together BigFlags instances.
		/// </summary>
		/// <param name="lhs"></param>
		/// <param name="rhs"></param>
		/// <returns></returns>
		public static BigFlags operator |( BigFlags lhs, BigFlags rhs )
		{
			var result = new BigFlags();
#if NUMERICS
			result.Value = lhs.Value | rhs.Value;
#else
			// BitArray is modified in place - always copy!
			result.Array = new BitArray( lhs.Bits ).Or( rhs.Bits );
#endif

			return result;
		}

		/// <summary>
		/// AND operator. And together BigFlags instances.
		/// </summary>
		/// <param name="lhs"></param>
		/// <param name="rhs"></param>
		/// <returns></returns>
		public static BigFlags operator &( BigFlags lhs, BigFlags rhs )
		{
			var result = new BigFlags();
#if NUMERICS
			result.Value = lhs.Value & rhs.Value;
#else
			// BitArray is modified in place - always copy!
			result.Array = new BitArray( lhs.Bits ).And( rhs.Bits );
#endif

			return result;
		}

		/// <summary>
		/// XOR operator. Xor together BigFlags instances.
		/// </summary>
		/// <param name="lhs"></param>
		/// <param name="rhs"></param>
		/// <returns></returns>
		public static BigFlags operator ^( BigFlags lhs, BigFlags rhs )
		{
			var result = new BigFlags();
#if NUMERICS
			result.Value = lhs.Value ^ rhs.Value;
#else
			// BitArray is modified in place - always copy!
			result.Array = new BitArray( lhs.Bits ).Xor( rhs.Bits );
#endif

			return result;
		}

		/// <summary>
		/// Equality operator.
		/// </summary>
		/// <param name="lhs"></param>
		/// <param name="rhs"></param>
		/// <returns></returns>
		public static bool operator ==( BigFlags lhs, BigFlags rhs )
		{
			return lhs.Equals( rhs );
		}

		/// <summary>
		/// Inequality operator.
		/// </summary>
		/// <param name="lhs"></param>
		/// <param name="rhs"></param>
		/// <returns></returns>
		public static bool operator !=( BigFlags lhs, BigFlags rhs )
		{
			return !( lhs == rhs );
		}
		#endregion ...Operators

		#region System.Object Overrides...

		/// <summary>
		/// Overridden. Returns a comma-separated string.
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
#if NUMERICS
			if( ZeroInit && Value == 0 )
			{
				return Fields[0].Name;
			}
#endif
			var names = new List<string>();
			for( int i = 0; i < Fields.Count; i++ )
			{
#if NUMERICS
				if( ZeroInit && i == 0 )
					continue;

				var bi = CreateValue( i );
				if( ( Value & bi ) ==  bi )
					names.Add( Fields[i].Name );
#else
				if( Bits[i] )
					names.Add( Fields[i].Name );
#endif
			}

			return String.Join( ", ", names );
		}

		/// <summary>
		/// Overridden. Compares equality with another object.
		/// </summary>
		/// <param name="obj"></param>
		/// <returns></returns>
		public override bool Equals( object obj )
		{
			if( obj is BigFlags )
			{
				return Equals( (BigFlags)obj );
			}

			return false;
		}

		/// <summary>
		/// Overridden. Gets the hash code of the internal BitArray.
		/// </summary>
		/// <returns></returns>
		public override int GetHashCode()
		{
#if NUMERICS
			return Value.GetHashCode();
#else
			int hash = 17;
			for( int i = 0; i < Bits.Length; i++ )
			{
				if( Bits[i] )
					hash ^= i;
			}

			return hash;
#endif
		}
		#endregion ...System.Object Overrides

		#region IEquatable<BigFlags> Members...

		/// <summary>
		/// Strongly-typed equality method.
		/// </summary>
		/// <param name="other"></param>
		/// <returns></returns>
		public bool Equals( BigFlags other )
		{
#if NUMERICS
			return Value == other.Value;
#else
			for( int i = 0; i < Bits.Length; i++ )
			{
				if( Bits[i] != other.Bits[i] )
					return false;
			}

			return true;
#endif
		}
		#endregion ...IEquatable<BigFlags> Members

		#region IComparable<BigFlags> Members...

		/// <summary>
		/// Compares based on highest bit set. Instance with higher
		/// bit set is bigger.
		/// </summary>
		/// <param name="other"></param>
		/// <returns></returns>
		public int CompareTo( BigFlags other )
		{
#if NUMERICS
			return Value.CompareTo( other.Value );
#else
			for( int i = Bits.Length - 1; i >= 0; i-- )
			{
				bool thisVal = Bits[i];
				bool otherVal = other.Bits[i];
				if( thisVal && !otherVal )
					return 1;
				else if( !thisVal && otherVal )
					return -1;
			}

			return 0;
#endif
		}
		#endregion ...IComparable<BigFlags> Members

		#region IComparable Members...

		int IComparable.CompareTo( object obj )
		{
			if( obj is BigFlags )
			{
				return CompareTo( (BigFlags)obj );
			}

			return -1;
		}
		#endregion ...IComparable Members

		#region IConvertible Members...

		/// <summary>
		/// Returns TypeCode.Object.
		/// </summary>
		/// <returns></returns>
		public TypeCode GetTypeCode()
		{
			return TypeCode.Object;
		}

		bool IConvertible.ToBoolean( IFormatProvider provider )
		{
			throw new NotSupportedException();
		}

		byte IConvertible.ToByte( IFormatProvider provider )
		{
#if NUMERICS
			return Convert.ToByte( Value );
#else
			throw new NotSupportedException();
#endif
		}

		char IConvertible.ToChar( IFormatProvider provider )
		{
			throw new NotSupportedException();
		}

		DateTime IConvertible.ToDateTime( IFormatProvider provider )
		{
			throw new NotSupportedException();
		}

		decimal IConvertible.ToDecimal( IFormatProvider provider )
		{
#if NUMERICS
			return Convert.ToDecimal( Value );
#else
			throw new NotSupportedException();
#endif
		}

		double IConvertible.ToDouble( IFormatProvider provider )
		{
#if NUMERICS
			return Convert.ToDouble( Value );
#else
			throw new NotSupportedException();
#endif
		}

		short IConvertible.ToInt16( IFormatProvider provider )
		{
#if NUMERICS
			return Convert.ToInt16( Value );
#else
			throw new NotSupportedException();
#endif
		}

		int IConvertible.ToInt32( IFormatProvider provider )
		{
#if NUMERICS
			return Convert.ToInt32( Value );
#else
			throw new NotSupportedException();
#endif
		}

		long IConvertible.ToInt64( IFormatProvider provider )
		{
#if NUMERICS
			return Convert.ToInt64( Value );
#else
			throw new NotSupportedException();
#endif
		}

		sbyte IConvertible.ToSByte( IFormatProvider provider )
		{
#if NUMERICS
			return Convert.ToSByte( Value );
#else
			throw new NotSupportedException();
#endif
		}

		float IConvertible.ToSingle( IFormatProvider provider )
		{
#if NUMERICS
			return Convert.ToSingle( Value );
#else
			throw new NotSupportedException();
#endif
		}

		string IConvertible.ToString( IFormatProvider provider )
		{
			return ToString();
		}

		object IConvertible.ToType( Type conversionType, IFormatProvider provider )
		{
			var tc = TypeDescriptor.GetConverter( this );

			return tc.ConvertTo( this, conversionType );
		}

		ushort IConvertible.ToUInt16( IFormatProvider provider )
		{
#if NUMERICS
			return Convert.ToUInt16( Value );
#else
			throw new NotSupportedException();
#endif
		}

		uint IConvertible.ToUInt32( IFormatProvider provider )
		{
#if NUMERICS
			return Convert.ToUInt32( Value );
#else
			throw new NotSupportedException();
#endif
		}

		ulong IConvertible.ToUInt64( IFormatProvider provider )
		{
#if NUMERICS
			return Convert.ToUInt64( Value );
#else
			throw new NotSupportedException();
#endif
		}
		#endregion ...IConvertible Members

		#region Public Interface...

		/// <summary>
		/// Checks <paramref name="flags"/> to see if all the bits set in
		/// that flags are also set in this flags.
		/// </summary>
		/// <param name="flags"></param>
		/// <returns></returns>
		public bool HasFlag( BigFlags flags )
		{
			return ( this & flags ) == flags;
		}

		/// <summary>
		/// Gets the names of this BigFlags enumerated type.
		/// </summary>
		/// <returns></returns>
		public static string[] GetNames()
		{
			return Fields.Select( x => x.Name ).ToArray();
		}

		/// <summary>
		/// Gets all the values of this BigFlags enumerated type.
		/// </summary>
		/// <returns></returns>
		public static BigFlags[] GetValues()
		{
			return FieldValues.ToArray();
		}

		/// <summary>
		/// Standard TryParse pattern. Parses a BigFlags result from a string.
		/// </summary>
		/// <param name="s"></param>
		/// <param name="result"></param>
		/// <returns></returns>
		public static bool TryParse( string s, out BigFlags result )
		{
			result = new BigFlags();
			if( String.IsNullOrEmpty( s ) )
				return true;

			var fieldNames = s.Split( ',' );
			foreach( var f in fieldNames )
			{
				var field = Fields.FirstOrDefault( x =>
					String.Equals( x.Name, f.Trim(),
					StringComparison.OrdinalIgnoreCase ) );
				if( null == field )
				{
					result = new BigFlags();
					return false;
				}
#if NUMERICS
				int i = Fields.IndexOf( field );
				result.Value |= CreateValue( i );
#else
				result.Bits.Set( Fields.IndexOf( field ), true );
#endif
			}

			return true;
		}

		//
		// Expose "enums" as public static readonly fields.
		// TODO: Replace this section with your "enum" values.
		//
		public static readonly BigFlags None;
		public static readonly BigFlags FirstValue;
		public static readonly BigFlags ValueTwo;
		public static readonly BigFlags ValueThree;
		public static readonly BigFlags ValueFour;
		public static readonly BigFlags ValueFive;
		public static readonly BigFlags ValueSix;
		public static readonly BigFlags LastValue;

		/// <summary>
		/// Expose flagged combinations as get-only properties.
		/// </summary>
		public static BigFlags FirstLast
		{
			get
			{
				return BigFlags.FirstValue | BigFlags.LastValue;
			}
		}
		#endregion ...Public Interface
	}

	/// <summary>
	/// Converts objects to and from BigFlags instances.
	/// </summary>
	public class BigFlagsConverter : TypeConverter
	{
		/// <summary>
		/// Can convert to string only.
		/// </summary>
		/// <param name="context"></param>
		/// <param name="destinationType"></param>
		/// <returns></returns>
		public override bool CanConvertTo( ITypeDescriptorContext context,
			Type destinationType )
		{
			return destinationType == typeof( String );
		}

		/// <summary>
		/// Can convert from any object.
		/// </summary>
		/// <param name="context"></param>
		/// <param name="sourceType"></param>
		/// <returns></returns>
		public override bool CanConvertFrom( ITypeDescriptorContext context,
			Type sourceType )
		{
			return true;
		}

		/// <summary>
		/// Converts BigFlags to a string.
		/// </summary>
		/// <param name="context"></param>
		/// <param name="culture"></param>
		/// <param name="value"></param>
		/// <param name="destinationType"></param>
		/// <returns></returns>
		public override object ConvertTo( ITypeDescriptorContext context,
			CultureInfo culture, object value, Type destinationType )
		{
			if( value is BigFlags && CanConvertTo( destinationType ) )
				return value.ToString();

			return null;
		}

		/// <summary>
		/// Attempts to parse <paramref name="value"/> and create and
		/// return a new BigFlags instance.
		/// </summary>
		/// <param name="context"></param>
		/// <param name="culture"></param>
		/// <param name="value"></param>
		/// <returns></returns>
		public override object ConvertFrom( ITypeDescriptorContext context,
			CultureInfo culture, object value )
		{
			var s = Convert.ToString( value );
			BigFlags result;
			BigFlags.TryParse( s, out result );

			return result;
		}
	}
}

Solution 6 - C#

In C#, one flexible way to represent a value that is sort of an enumeration but more flexible is to represent it as a static class with precooked values available, like this:

public sealed class WebAgentPermission
{
    private long ID;

    public static readonly WebAgentPermission
        ViewRuleGroup = new WebAgentPermission { ID = 1 };
    public static readonly WebAgentPermission
        AddRuleGroup  = new WebAgentPermission { ID = 2 };

    private WebAgentPermission() { } 

    // considerations: override equals/gethashcode, probably override tostring,
    // maybe implicit cast to/from long, maybe other stuff
}

Alternatively, just split the thing up; it looks like you could, if you really tried.

Solution 7 - C#

If I were in control of this application, I would probably come up with a common set of permissions (View, Add, Edit, Delete, Upload/Import) and a set of resources (Users, Roles, Rules, etc). On the web page find the resource type associated with that page and then check the permissions. Perhaps something like:

Permissions perms = agent.GetPermissions(ResourceType.User);
if((perms & Permissions.View) == Permissions.View) { /* do work */ }

or

Permissions perms = agent.Permissions[ResourceType.User];
if((perms & Permissions.View) == Permissions.View) { /* do work */ }

or even

if(agent.IsAuthorized(ResourceType.User, Permissions.View)) { /* do work */ }

You have a couple of permissions that don't make sense with everything else (Assign Permissoins to user, to name one). I'm not sure how I would handle that based on how little I know the problem.

Solution 8 - C#

I have not been in this situation.

Here is what I think, create separate enums for each of the category & accept those as parameters.

RuleGroupPermission
    None = 0
    ViewRuleGroup = 1,
    AddRuleGroup = 2,
    EditRuleGroup = 4,
    DeleteRuleGroup = 8,

LocationOperations
    None = 0
    Add = 1
    View = 2
    Delete = 4

void setPermission(RuleGroupPermission ruleGroupOpsAllowed, LocationOperations locationOptions)
{
   ...
}

EDIT: Look at how messagebox.show does it. OK, OKCancel separated from Question, Information, Exclamation.

Solution 9 - C#

How about... having the permissions in a list?

They can be loaded from a database when the program starts, and modified without redeploying the system!

public class Permission
{
	public string Description;
	public string Code;
	public bool IsSet;
}

List<Permission> permissions;

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMatthew VinesView Question on Stackoverflow
Solution 1 - C#Fredrik MörkView Answer on Stackoverflow
Solution 2 - C#the.jxcView Answer on Stackoverflow
Solution 3 - C#arbiterView Answer on Stackoverflow
Solution 4 - C#DavidMokshaView Answer on Stackoverflow
Solution 5 - C#Bruce PiersonView Answer on Stackoverflow
Solution 6 - C#mqpView Answer on Stackoverflow
Solution 7 - C#TalljoeView Answer on Stackoverflow
Solution 8 - C#shahkalpeshView Answer on Stackoverflow
Solution 9 - C#Graham LaightView Answer on Stackoverflow