当位掩码(标志)枚举变得过大时应该怎么办?

98
我在我的应用程序中有一组非常大的权限,我使用Flags枚举来表示。现在它已经接近long数据类型的实际上限,我被迫考虑很快转换到另一种结构的策略。我可以将此列表分解成较小的部分,但这已经是我们应用程序的整体权限的子集,基于应用程序的布局,我们在管理权限时广泛使用此区分以进行显示。如果可能的话,我不想此时重新审视该代码。是否有其他人遇到过此问题?你们是如何克服的?一般示例就可以了,但如果有任何特定于c#的技巧,那我最感兴趣。
也许不必要,但以下是我正在处理的应用程序部分当前定义的权限列表。
//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
}

83
为了更加清晰,我通常使用(1 << 0),(1 << 1),.. (1 << 57)表示我的标志位。这样更容易理解,并且更难出错。不过这并没有回答你的问题。 - Talljoe
谢谢,我已经尝试了几种类似的方法,但我总是得到"无法使用计算值为枚举错误"。 - Matthew Vines
4
我认为 ^ 不是你想的意思。 - Eric Lippert
30
如果你想进行长的位移操作,请告诉编译器你的意图。(1L << 40) 可以正常运行。 - Eric Lippert
1
好吧,这就是我匆忙完成的后果,再次感谢你指出我犯的愚蠢错误 :) - Matthew Vines
显示剩余4条评论
10个回答

47

我看到至少有几个不同的枚举值...

我首先想到的是通过将权限分组成逻辑组(RuleGroupPermissionsRulePermissionsLocationPermissions等),然后拥有一个类(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...
}

2
我喜欢这个解决方案,虽然它们非常标准,但我认为存在足够的差异使其有点繁琐。最终,我认为我只能忍受它并将这个大型项目拆分成更小的部分,并重新设计我的应用程序的一些部分来处理这些更改。我只是希望有一个神奇的方法我还没有发现。谢谢你的时间。 - Matthew Vines
3
在这种情况下,那是一个相当不错的解决方案。不幸的是,除了“尝试将其拆分为枚举含义最好的部分”之外,它并没有提供处理类似问题的通用方法。 - PPC
1
如果你的可能操作不仅仅是CRUD,这就变得棘手起来了,你可能需要多个权限枚举。我无法想象尝试将所有这些内容存储到数据库中,每次需要用户权限时都要加入大量表格。 - perustaja

31

语言文档说:

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

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

然而... 更新:

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

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

Int32只是默认数据类型!实际上,您可以指定Int64。

public enum MyEnumType : Int64

允许最多64个值。但是这肯定是最大值,在那之后你将需要重新设计。如果不太了解您解决方案的其他内容,我无法确定什么最适合。但是特权标识符的数组(或哈希映射)可能是最自然的方法。


你发布的引用来自一位评论者,我认为他是错误的。Flag属性根本不指定任何类型信息。虽然如果您没有显式指定类型,则枚举默认为Int32。 - Matthew Vines
此答案中引用的引语似乎不再出现在它所在的页面上。 - Panzercrisis
1
@MatthewVines指定了public enum WebAgentPermission : long,这与使用Int64相同。 - Ogglas
@leigero 对我来说,这听起来像是设计上的问题。也许可以考虑将这些值分解或更改结构?例如,如果是用于权限,请考虑使用声明,除非有特定原因要使用二进制来表示此结构。即使必须使用二进制,您仍然可以在多个字段之间执行位运算(例如,((p.group | p.location) & MY_PERMISSION) == MY_PERMISSION),你知道吗? - Sinaesthetic
9
使用 enum MyEnumType : Int64 与移位运算符时,需要注意以下提示:在 [Flags] 公共枚举类型中,确保使用 1**L** 来告诉编译器这是一个长整型/Int64,比如 A = 1L << 0, B = 1L << 1,。否则编译器会将其解释为 int32,在第32位后会重新从1开始,从而给其他枚举名称带来重复的值。 - Erik

17

虽然这不是你问题的答案,但我有一个相关的建议:我们使用位移来指定数值,像这样:

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

前十个并不是很重要,但在那之后变得非常方便。


14

这是一个比我想象中更常见的问题,我将CSS类表示为标志类型,但可能超过了64个可能性。我将所学到的全部内容整理成一个可重用的模式,不过由于它是结构体,所以仍然需要复制粘贴。

这是“BigFlags”枚举类型。它使用中的BigInteger,或者如果无法引用该程序集,则可以使用回退方案,使用BitArray并关闭NUMERICS预处理器指令。

它的行为非常像Flags枚举,甚至定义了HasFlag(...)、GetNames()、GetValues()、TryParse(...)、TypeConverter和IConvertible等功能。由于它定义了TypeConverter和IConvertible,因此也适合存储在数据存储中,但始终作为字符串或文本数据类型。

您可以将“枚举”值公开为public static readonly成员。组合的枚举值被公开为只读属性。

要使用它,请复制并粘贴代码,然后在“BigFlags”上执行搜索和替换操作,使用自己的结构体名称,然后删除“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;
        }
    }
}

这个 SmartEnum 库做了类似的事情 - https://github.com/ardalis/SmartEnum - rothschild86

13

您可以查看BitArray类。也许将来您会用到它。


这是一个不错的选择,但当然你应该将其封装在一个类中,并像其他帖子建议的那样为所有这些位命名。 - Doug McClean
BitArray类非常理想 - 定义一个带有公共枚举的类来表示权限(从0开始的相邻整数),并拥有一个查询函数,该函数返回私有BitArray成员由权限枚举索引的值。这将有效地处理任意数量的权限。 - Stephen C. Steel
好的。并且它在1.0版中得到了支持。 - HelloSam
1
@arbiter。这是一个有趣的解决方案。如果我想在值存储在数据库中时检查位的存在怎么办? - Paul Fleming

5
在C#中,一种灵活的表示类似于枚举但更加灵活的值的方式是将其表示为一个静态类,并提供预定义的可用值,如下所示:
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
}

或者,只需将它分开;如果你真的尝试过,似乎你可以做到。


1
这也是一个不错的看待问题的方式,直到现在我都没有考虑过。感谢您的建议。 - Matthew Vines

4

如果我掌控这个应用程序,我可能会制定一组常见的权限(查看、添加、编辑、删除、上传/导入)和一组资源(用户、角色、规则等)。在网页上找到与该页面相关联的资源类型,然后检查权限。可能是这样的:

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

您拥有一些权限,这些权限与其他所有内容都不太合适(例如分配用户权限)。基于我对该问题的了解非常少,我不确定该如何处理。


如果没有其他的话,对于你上面的评论点个赞,感谢你的时间。 - Matthew Vines

2

我没有遇到过这种情况。

我的建议是,为每个类别创建单独的枚举并将其作为参数接受。

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是如何做到的。确定、取消与问题、信息、警告分开。


1

对于面临类似问题的其他人,我建议使用InfiniteEnumFlags库, 它具有所有dotnet枚举标志功能以及更多功能。


0
怎么样……将权限放在列表里呢?
在程序启动时,它们可以从数据库中加载,并且可以在不重新部署系统的情况下进行修改!
public class Permission
{
    public string Description;
    public string Code;
    public bool IsSet;
}

List<Permission> permissions;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接