Was tun, wenn die Aufzählung der Bitmasken (Flags) zu groß wird?

78

Ich habe eine sehr große Anzahl von Berechtigungen in meiner Anwendung, die ich mit einer Flags-Aufzählung darstelle. Es nähert sich schnell der praktischen Obergrenze des langen Datentyps. Und ich bin gezwungen, eine Strategie zu entwickeln, um bald zu einer anderen Struktur überzugehen. Jetzt könnte ich diese Liste in kleinere Teile zerlegen. Dies ist jedoch bereits eine Teilmenge der Gesamtberechtigungen für unsere Anwendung, basierend auf unserem Anwendungslayout. Wir verwenden diese Unterscheidung ausgiebig für Anzeigezwecke bei der Verwaltung von Berechtigungen, und ich möchte diesen Code zu diesem Zeitpunkt lieber nicht erneut aufrufen, wenn ich ihn vermeiden kann.

Ist noch jemand auf dieses Problem gestoßen? Wie bist du daran vorbei gekommen? Allgemeine Beispiele sind in Ordnung, aber ich interessiere mich am meisten für ein spezifisches Beispiel, wenn es sprachspezifische Tricks gibt, die ich anwenden kann, um die Arbeit zu erledigen.

Möglicherweise nicht erforderlich, aber hier ist die Liste der Berechtigungen, die derzeit für den Teil der App definiert sind, mit dem ich mich befasse.

//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
}
Matthew Vines
quelle
69
Aus Gründen der Übersichtlichkeit verwende ich normalerweise: (1 << 0), (1 << 1), .. (1 << 57) für meine Flags. Einfacher zu verstehen und schwieriger, den Wert falsch zu verstehen. Beantwortet Ihre Frage jedoch nicht.
Talljoe
danke, ich hatte ein paar Möglichkeiten ausprobiert, um etwas Ähnliches zu tun, aber ich konnte immer keinen berechneten Wert für Aufzählungsfehler verwenden.
Matthew Vines
4
Ich denke, ^ bedeutet nicht, was du denkst, dass es bedeutet.
Eric Lippert
27
Wenn Sie eine lange Bitverschiebung vornehmen möchten, teilen Sie dem Compiler mit, dass Sie dies möchten. (1L << 40) sollte gut funktionieren.
Eric Lippert
1
Nun, explizit, das ist es, was ich dafür bekomme, dass ich mich durch die Sache beeilt habe. Nochmals vielen Dank, dass ich auf etwas Dummes hingewiesen habe, das ich getan habe :)
Matthew Vines

Antworten:

41

Ich sehe dort Werte von mindestens einer Handvoll verschiedener Aufzählungen ...

Mein erster Gedanke war , das Problem durch die Spaltung der Berechtigungen auf in logischen Gruppen zu nähern ( RuleGroupPermissions, RulePermissions, LocationPermissions, ...) und dann eine Klasse mit ( WebAgentPermissions) einer Eigenschaft für jede Berechtigung Enumerationstyp auszusetzen.

Da sich die Berechtigungswerte zu wiederholen scheinen, könnten Sie am Ende wahrscheinlich mit einer einzigen Aufzählung davonkommen:

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

Lassen Sie die WebAgentPermissionsKlasse dann eine Eigenschaft für jeden Bereich verfügbar machen, in dem Berechtigungen festgelegt werden sollen.

class WebAgentPermissions
{
    public Permissions RuleGroup { get; set; }
    public Permissions Rule { get; set; }
    public Permissions Location { get; set; }
    // and so on...
}
Fredrik Mörk
quelle
2
Ich mag das, und obwohl sie ziemlich normal sind, denke ich, dass es genug Abweichungen gibt, um diese Lösung etwas umständlich zu machen. Am Ende denke ich, ich muss es nur aufsaugen und dieses Biest in kleinere Stücke zerlegen und ein paar Abschnitte meiner Anwendung neu konstruieren, um die Änderungen zu handhaben. Ich hatte nur gehofft, dass da draußen eine magische Kugel war, die ich nicht gefunden hatte. Vielen Dank für Ihre Zeit.
Matthew Vines
2
Das ist in diesem Fall eine ziemlich gute Lösung. Leider gibt es keine generische Möglichkeit, mit ähnlichen Problemen umzugehen, außer "versuchen Sie, sie so gut wie möglich aufzuteilen".
PPC
25

Sprachdokumentation sagt:

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

"Der zugrunde liegende Typ ist Int32, und daher ist das maximale Einzelbit-Flag 1073741824, und offensichtlich gibt es insgesamt 32 Flags für jede Aufzählung."

Jedoch ... AKTUALISIERT:

Der Kommentator ist korrekt. Schau dir das an:

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

Int32 ist nur der Datentyp DEFAULT! Tatsächlich können Sie Int64 angeben.

public enum MyEnumType : Int64

... bis zu 64 Werte zulassen. Aber das scheint sicherlich das Maximum zu sein, danach werden Sie sich mit Re-Engineering befassen. Ohne zu viel über den Rest Ihrer Lösung zu wissen, kann ich nicht genau sagen, was passen könnte. Ein Array (oder eine Hash-Map) von Berechtigungskennungen ist jedoch wahrscheinlich der natürlichste Ansatz.

the.jxc
quelle
Das Zitat, das Sie gepostet haben, stammt von einem Kommentator, und ich glaube, er ist falsch. Das Flag-Attribut gibt überhaupt keine Typinformationen an. Enums sind standardmäßig Int32, wenn Sie keinen Typ explizit angeben.
Matthew Vines
4
@PatrickM leider, obwohl das eine großartige Anekdote ist, ist es nicht Realität und ich brauche mehr.
Leigero
Das Zitat, auf das in dieser Antwort verwiesen wird, scheint nicht mehr auf der Seite zu sein, von der es stammt.
Panzercrisis
1
@MatthewVines hat angegeben public enum WebAgentPermission : longund das ist das gleiche wie mitInt64
Ogglas
@leigero Das schreit Designgeruch für mich tbh. Vielleicht versuchen Sie, diese Werte aufzubrechen oder die Struktur zu ändern? Wenn es sich beispielsweise um Berechtigungen handelt, sollten Sie möglicherweise stattdessen Ansprüche verwenden, es sei denn, es gibt einen bestimmten Grund, für diese Struktur Binärdateien zu verwenden. Selbst wenn Sie Binärdateien verwenden müssen, können Sie dennoch bitweise Operationen über mehrere Felder hinweg ausführen (z. B. ((p.group | p.location) & MY_PERMISSION) == MY_PERMISSION), wissen Sie?
Sinaesthetic
13

Sie können die BitArray- Klasse überprüfen . Vielleicht wirst du es in Zukunft benutzen.

Schiedsrichter
quelle
Dies ist eine gute Option, aber natürlich sollten Sie sie in eine Klasse einkapseln, die all diesen Bits Namen gibt, wie einige der anderen Poster vorgeschlagen haben.
Doug McClean
Die BitArray-Klasse ist ideal - definieren Sie eine Klasse mit einer öffentlichen Aufzählung für die Berechtigungen (nur aufeinanderfolgende Ganzzahlen ab 0) und verfügen Sie über eine Abfragefunktion, die nur die Werte des privaten BitArray-Mitglieds zurückgibt, das durch die Berechtigungsaufzählung indiziert wird. Dadurch wird eine beliebige Anzahl von Berechtigungen effizient verarbeitet.
Stephen C. Steel
Nett. Und es wird bereits in 1.0 unterstützt.
HelloSam
1
@Schiedsrichter. Dies ist eine interessante Lösung. Was ist, wenn ich prüfen möchte, ob ein Bit vorhanden ist, wenn der Wert in einer Datenbank gespeichert ist?
Paul Fleming
10

Dies stellte sich als häufigeres Problem heraus, als ich dachte, da ich CSS-Klassen als Flags-Typen darstellte und es mehr als 64 Möglichkeiten gab. Ich habe alles, was ich aus diesem Prozess gelernt habe, in ein wiederverwendbares Muster umgewandelt, obwohl es sich, da es sich um eine Struktur handelt, um ein Muster vom Typ Kopieren und Einfügen handelt.

Dies ist der BigFlags"Aufzählungstyp". Es wird entweder BigIntegervon verwendet System.Numerics, oder wenn Sie nicht auf diese Assembly verweisen können, gibt es einen Fallback, der BitArraydurch einfaches Deaktivieren der NUMERICSPräprozessor-Direktive verwendet wird.

Es verhält sich bemerkenswert wie ein FlagsEnum, auch solche Dinge wie die Definition HasFlag(...), GetNames(), GetValues(), TryParse(...), a TypeConverter, IConvertibleusw. Da es sich um eine nicht definiert TypeConverterund IConvertiblees ist auch für die in einem Datenspeicher zu speichern, wenn auch immer als String oder Text - Datentyp.

Sie legen die "enum" -Werte als public static readonlyMitglieder offen . Kombinierte Aufzählungswerte werden als Nur-Get-Eigenschaften angezeigt.

Um es zu verwenden, kopieren Sie den Code und fügen Sie ihn ein. BigFlagsFühren Sie dann eine Suche durch und ersetzen Sie ihn durch Ihren Strukturnamen. Löschen Sie dann die Aufzählungen im TODOAbschnitt und fügen Sie Ihre Werte hinzu.

Hoffe, jemand findet es nützlich.

#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;
        }
    }
}
Bruce Pierson
quelle
5

In C # besteht eine flexible Möglichkeit, einen Wert darzustellen, der eine Art Aufzählung darstellt, aber flexibler ist, darin, ihn als statische Klasse mit vorgekochten Werten wie folgt darzustellen:

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
}

Alternativ können Sie das Ding auch einfach aufteilen. es sieht so aus, als könntest du es, wenn du es wirklich versuchst.

mqp
quelle
1
Dies ist eine anständige Sichtweise auf dieses Problem, und keine, die ich bisher in Betracht gezogen hatte. Danke für deinen Beitrag.
Matthew Vines
5

Keine Antwort auf Ihre Frage, sondern ein verwandter Vorschlag: Wir verwenden Bitshifting, um die numerischen Werte wie folgt anzugeben:

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

Nicht so wichtig für die ersten zehn, aber danach wird es wirklich praktisch.

DavidMoksha
quelle
4

Wenn ich die Kontrolle über diese Anwendung hätte, würde ich wahrscheinlich eine Reihe allgemeiner Berechtigungen (Anzeigen, Hinzufügen, Bearbeiten, Löschen, Hochladen / Importieren) und eine Reihe von Ressourcen (Benutzer, Rollen, Regeln usw.) entwickeln. Suchen Sie auf der Webseite den Ressourcentyp, der dieser Seite zugeordnet ist, und überprüfen Sie die Berechtigungen. Vielleicht so etwas wie:

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

oder

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

oder auch

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

Sie haben einige Berechtigungen, die bei allem anderen keinen Sinn ergeben (Berechtigungen dem Benutzer zuweisen, um eine zu benennen). Ich bin mir nicht sicher, wie ich damit umgehen würde, basierend darauf, wie wenig ich das Problem kenne.

Talljoe
quelle
+1 für Ihren Kommentar oben, wenn nichts anderes, danke für Ihre Zeit.
Matthew Vines
1

Ich war nicht in dieser Situation.

Hier ist, was ich denke, erstellen Sie separate Aufzählungen für jede der Kategorien und akzeptieren Sie diese als Parameter.

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

BEARBEITEN: Sehen Sie sich an, wie messagebox.show das macht. OK, OKCancel getrennt von Frage, Information, Ausruf.

shahkalpesh
quelle