简体   繁体   中英

Remove privilege enum flags the right way in C#

I have an enum type for user privileges that looks like this:

[Flags]
public enum UserPrivileges : byte
{
    None = 0,                                     // 0000 0000
    View = 1 << 0,                                // 0000 0001
    Import = 1 << 1,                              // 0000 0010
    Export = 1 << 2,                              // 0000 0100
    Supervisor = View | Import | Export | 1 << 3, // 0000 1111
    Admin = Supervisor | 1 << 4                   // 0001 1111
}

These values are bound to CheckBoxes in the GUI with a value converter. (I wanted to do this as generic as possible because there are also different privileges [eg EmployeePrivileges])

public class ByteFlagsEnumValueConverter : IValueConverter
{
    private byte _targetValue;

    public object Convert(object value, Type targetType,
                          object parameter, CultureInfo culture)
    {
        var mask = (byte)parameter;
        _targetValue = (byte)value;
        return ((mask | _targetValue) == _targetValue);
    }

    public object ConvertBack(object value, Type targetType,
                              object parameter, CultureInfo culture)
    {
        var mask = (byte)parameter;

        if ((bool)value)
        {
            _targetValue |= mask;
        }
        else
        {
            // Get next superflag for mask (e.g. 0110 -> 1111)
            var b = mask;
            b--;
            b |= (byte)(b >> 1);
            b |= (byte)(b >> 2);
            b |= (byte)(b >> 4);
            b |= (byte)(b >> 8);

            // if you remove a superflag (e.g. 1111) also remove
            // everything higher than this flag
            if (mask == b || mask == 1)
                _targetValue &= (byte)(mask >> 1);
            else
                // ????????
        }

        return Enum.Parse(targetType, _targetValue.ToString());
    }
}

This works really fine for displaying and adding privileges to the user in the GUI. Also it works for removing Superflags like Supervisor (all flags >= Supervisor get removed, the other flags don't change).

The problem is when I uncheck Import for example, I want to remove all Superflags (Supervisor, Admin) but would like to keep the other flags (View, Export).

0001 1111 // Admin
0000 0010 // Import
---------
0000 0101 // View | Export

But I haven't come up with an good idea how to accomplish this. Anyboy who has a good solution for this?

If I understand what you want , this should do the job

    byte tmp = 1 << 3 | 1 << 4;
    byte removeSuperFlagMask = (byte) (~tmp);

    byte notSuperflagsMask = 1 << 3 | 1 << 2 | 1 << 1;
    if ((valueToRemove & notSuperflagsMask) != 0)
    {
        newValue = (byte)(removeSuperFlagMask & currentValue & ~valueToRemove);
    }

If I understood correctly you want to remove Supervisor and Admin so:

UserPrivileges newPrivileges = (UserPrivileges)(((byte)currentPrivileges) & 7;

It will perform like this:

0001 1111 // Admin
0000 0111 // 7 flag
---------
0000 0111 // Remain only where 7 bit was set.

Another example

0000 0011 // View | Import
0000 0111 // 7 flag
---------
0000 0011 // Remain only where 7 bit was set.

By remain I mean that where 7 flag is set it will persist the value in the result (beein 0 or 1 ). And where the 7 flag is 0 it will kill the value to 0 .

Mfz brought me on the right way but, it wasn't generic enough for me, so I came up with another solution:

    public object ConvertBack(object value, Type targetType,
                              object parameter, CultureInfo culture)
    {
        var mask = (byte)parameter;

        if ((bool)value)
        {
            _targetValue |= mask;
        }
        else
        {
            if (IsSuperFlag(mask) && mask != 1)
                _targetValue &= (byte)(mask >> 1);
            else
            {
                // Get all flags from enum type that are no SuperFlags
                var flags = Enum.GetValues(targetType).Cast<Enum>();
                flags = flags.Where(x => !IsSuperFlag(System.Convert.ToByte(x)));

                long nonSuperFlags = 0;

                foreach (var flag in flags)
                {
                    nonSuperFlags |= System.Convert.ToByte(flag);
                }

                // Now only remove everything except the nonSuperFlags
                // and then remove the mask
                _targetValue &= (byte)(~(_targetValue ^ nonSuperFlags | mask));

            }
        }

        return Enum.Parse(targetType, _targetValue.ToString());
    }

    private bool IsSuperFlag(byte flag)
    {
        var b = flag;
        b--;
        b |= (byte)(b >> 1);
        b |= (byte)(b >> 2);
        b |= (byte)(b >> 4);
        b |= (byte)(b >> 8);

        return b == flag;
    }

I simply got all non superflags for the enum type and them removed only the superflags and afterwards removed the flag.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM