Що робити, коли бітова маска (прапори) enum стає занадто великою


81

У моїй програмі є дуже великий набір дозволів, які я представляю із переліком прапорів. Він швидко наближається до практичної верхньої межі довгого типу даних. І я змушений найближчим часом запропонувати стратегію переходу до іншої структури. Тепер я міг би розбити цей список на більш дрібні частини, однак, це вже лише підмножина загальних дозволів для нашої програми, виходячи з макету наших програм. Ми широко використовуємо цю відмінність для відображення під час управління дозволами, і я б волів не переглядати цей код зараз, якщо зможу цього уникнути.

Хто-небудь ще стикався з цим питанням? Як ти пройшов це? Загальні приклади - це добре, але мене найбільше цікавить приклад 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
}

70
Для наочності я зазвичай використовую: (1 << 0), (1 << 1), .. (1 << 57) для своїх прапорів. Легше зрозуміти і важче помилитися. Однак не відповідає на ваше запитання.
Talljoe

дякую, я спробував кілька способів зробити щось подібне, але мені завжди не вдається використовувати обчислене значення для помилки перерахувань.
Matthew Vines

4
Я думаю, що ^ означає не те, що, на вашу думку, це означає.
Ерік Ліпперт

28
Якщо ви хочете зробити довгий бітовий зсув, скажіть компілятору, що ви хочете. (1L << 40) повинен працювати нормально.
Ерік Ліпперт

1
Ну, пояснювальний, ось що я отримую за поспіх через це, ще раз дякую за те, що я вказав на щось дурне, що я зробив :)
Matthew Vines

Відповіді:


41

Я бачу там принаймні кілька різних переліків ...

Моя перша думка була підійти до вирішення проблеми шляхом поділу прав доступу в логічні групи ( RuleGroupPermissions, RulePermissions, LocationPermissions...), а потім мати клас (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

2
У цьому випадку це досить гарне рішення. На жаль, це не дає загального способу вирішення подібних проблем, за винятком "спробуйте розділити його якнайкраще, що означає перелік".
PPC

Це стає складним завданням, якщо ваші можливі операції не просто CRUD, вам може знадобитися кілька переліків дозволів. Я не можу уявити, щоб намагатися зберегти все це в базі даних, ви б приєдналися до тонни таблиць кожного разу, коли вам потрібні дозволи вашого користувача.
perustaja

25

Мовна документація говорить:

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

4
@PatrickM, на жаль, хоча це чудовий анекдот, це не реальність, і мені потрібно більше.
leigero

Цитата, на яку посилається ця відповідь, більше не знаходиться на тій сторінці, з якої вона взята.
Panzercrisis

1
@MatthewVines вказав, public enum WebAgentPermission : longі це те саме, що використовуватиInt64
Огглас

@leigero Це кричить дизайнерський запах мені tbh. Можливо, розглянути можливість розбити ці цінності або змінити структуру? Наприклад, якщо це стосується дозволів, можливо, замість цього використовуйте претензії, якщо немає особливої ​​причини використовувати двійковий файл для цієї структури. Навіть якщо вам потрібно використовувати двійковий файл, ви все одно можете виконувати побітові операції в декількох полях (наприклад ((p.group | p.location) & MY_PERMISSION) == MY_PERMISSION), як ви знаєте?
Sinaesthetic

14

Ви можете перевірити клас BitArray . Можливо, ви будете використовувати його в майбутньому.


Це хороший варіант, але, звичайно, ви повинні інкапсулювати його в клас, який дає імена всім цим бітам, як пропонували інші плакати.
Дуг МакКлін

Клас BitArray ідеальний - визначте клас із загальнодоступним переліченням дозволів (лише послідовні цілі числа, що починаються з 0), і має функцію запиту, яка просто повертає значення приватного члена BitArray, проіндексованого переліком дозволів). Це дозволить ефективно обробляти довільну кількість дозволів.
Stephen C. Steel

Приємно. І це підтримується ще в 1.0.
HelloSam

1
@arbiter. Це цікаве рішення. Що робити, якщо я хочу перевірити наявність біта, коли значення зберігається в БД?
Пол Флемінг

10

Це виявилося більш поширеною проблемою, ніж я думав, що це буде, де я представляв класи CSS як типи прапорів, і було більше 64 можливостей. Я взяв усе, що дізнався з цього процесу, і перетворив його на шаблон багаторазового використання, хоча, оскільки це структура, це шаблон типу copy-and-paste.

Це BigFlags"перелічений тип". Він використовує або BigIntegerз System.Numerics, або якщо немає можливості посилатися на цю збірку, є резервний варіант, який використовується BitArray, просто вимкнувшиNUMERICS директиву препроцесора.

Він веде себе чудово , як Flagsперерахування, навіть визначаючи такі речі , як HasFlag(...), GetNames(), GetValues(), TryParse(...), A 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;
        }
    }
}

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

5

Не відповідь на ваше запитання, а відповідна пропозиція: ми використовуємо бітшифтинг, щоб вказати числові значення, наприклад:

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

Не так важливо для перших десяти, але після цього це стає дуже зручно.


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

У вас є пара дозволів, які не мають сенсу для всього іншого (Призначте Permissoins користувачеві, щоб назвати одного). Я не впевнений, як би з цим впорався, виходячи з того, наскільки мало я знаю проблему.


+1 за ваш коментар вище, якщо нічого іншого, дякую за ваш час.
Matthew Vines

1

Я не був у цій ситуації.

Ось що я думаю, створіть окремі переліки для кожної категорії та прийміть їх як параметри.

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: Подивіться, як це робить messagebox.show. Добре, ОК Скасувати, відокремлено від Питання, Інформація, Вигук.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.