位掩码(标志)枚举变得太大时该怎么办

在我的应用程序中,我有一个非常大的权限集合,用Flags枚举表示。 它正在快速接近长数据types的实际上限。 我不得不提出一个策略,以尽快过渡到不同的结构。 现在,我可以将这个列表分解成更小的部分,但是,根据我们的应用程序布局,这已经只是我们应用程序整体权限的一个子集。 在pipe理权限时,我们广泛使用这种区别作为显示目的,如果我能够避免,我宁愿不必在此时重新访问该代码。

有其他人遇到这个问题? 你是怎么过去的? 一般的例子都很好,但我最感兴趣的是ac#的具体例子,如果有任何语言特定的技巧,我可以用来完成工作。

可能不是必需的,但这里是目前为我正在处理的应用程序部分定义的权限列表。

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

我看到至less有一些不同的枚举在那里值…

我的第一个想法是通过在逻辑组( RuleGroupPermissionsRulePermissionsLocationPermissions ,…)中分割权限,然后让每个权限枚举types暴露一个属性( WebAgentPermissions )来解决问题。

由于权限值看起来是重复的,所以最后可能只有一个枚举:

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

然后让WebAgentPermissions类为每个要设置权限的区域公开一个属性;

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

语言文档说:

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

“底层的types是Int32,所以最大的单个位标志是1073741824,显然每个枚举总共有32个标志。

但是…更新:

评论者是正确的。 看看这个:

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

Int32只是DEFAULT数据types! 实际上你可以指定Int64。

 public enum MyEnumType : Int64 

…允许多达64个值。 但是,这似乎是最大的,之后,你将在重新devise。 如果不太了解其他解决scheme,我不能确切地说出适合的scheme。 但是特权标识符的数组(或散列图)可能是最自然的方法。

你可以检查BitArray类。 也许你会在将来使用它。

在C#中,一种灵活的方式来表示一个枚举types但更灵活的值,就是将其表示为一个静态类,并具有预先存储的值,如下所示:

 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 } 

或者,把事情分开。 它看起来像你可以,如果你真的尝试。

如果我在控制这个应用程序,我可能会想出一个共同的权限(查看,添加,编辑,删除,上传/导入)和一组资源(用户,angular色,规则等)。 在网页上find与该页面关联的资源types,然后检查权限。 也许是这样的:

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

要么

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

甚至

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

您有一些权限与其他所有权限无关(为用户指定Permissoins,命名一个)。 我不确定我将如何处理这个问题,因为我知道这个问题有多less。

原来,这是一个比我想象的更常见的问题,在那里我将CSS类表示为标志types,并且有超过64种可能性。 我把我从这个过程中学到的所有东西都变成了一个可重用的模式,尽pipe它是一个结构体,它是一个复制粘贴的模式。

这是BigFlags “枚举types”。 它使用来自System.Numerics BigInteger ,或者如果无法引用该程序集,则只需closuresNUMERICS预处理器指令即可使用BitArray

它的行为非常像Flags enum,甚至定义了HasFlag(...)GetNames()GetValues()TryParse(...)TypeConverterIConvertible等等。因为它定义了一个TypeConverterIConvertible它也适用于存储在数据存储中,尽pipe总是作为string或文本数据types。

您将“枚举”值public static readonly成员。 组合的枚举值暴露为只读属性。

要使用它,请复制并粘贴代码,然后执行search并使用结构名称replaceBigFlags ,然后删除TODO部分中的枚举并添加值。

希望有人认为它有用。

 #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; } } } 

我没有遇到这种情况。

这是我的想法,为每个类别创build单独的枚举并接受这些参数。

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

编辑:看看如何messagebox.show做到这一点。 OK,OK取消问题,信息,惊叹号。