Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enum.HasFlag, why no Enum.SetFlag?

Tags:

c#

enums

flags

I have to build an extension method for each flag type I declare, like so:

public static EventMessageScope SetFlag(this EventMessageScope flags, 
    EventMessageScope flag, bool value)
{
    if (value)
        flags |= flag;
    else
        flags &= ~flag;

    return flags;
}

Why isn't there an Enum.SetFlag like there is an Enum.HasFlag?

Also, why does this not work always?

public static bool Get(this EventMessageScope flags, EventMessageScope flag)
{
    return ((flags & flag) != 0);
}

For example, if I have:

var flag = EventMessageScope.Private;

And check it like:

if(flag.Get(EventMessageScope.Public))

Where EventMessageScope.Public really is EventMessageScope.Private | EventMessageScope.PublicOnly, it returns true.

When it's not, because Private is not public, it's just half public.

The same goes for:

if(flag.Get(EventMessageScope.None))

Which returns false, except the scope is actually None (0x0), when it should always return true?

like image 310
bevacqua Avatar asked May 01 '11 19:05

bevacqua


2 Answers

Why isn't there an Enum.SetFlag like there is an Enum.HasFlag?

HasFlag as a bitwise operation required more complicated logic and repeating the same flag twice

 myFlagsVariable=    ((myFlagsVariable & MyFlagsEnum.MyFlag) ==MyFlagsEnum.MyFlag );

so MS decided to implement it.

SetFlag and ClearFlag are concise in C#

    flags |= flag;// SetFlag

    flags &= ~flag; // ClearFlag 

but unfortunately not intuitive. Every time I need to set (or clear) a flag, I'm spending a few seconds (or minutes) to think: what is the name of the method? Why is it not shown in intellisense? Or no, I have to use bitwise operations. Note, that some developers will also ask: what is a bitwise operation?

Should SetFlag and ClearFlag extensions be created - YES to appear in intellisense.

Should SetFlag and ClearFlag extensions be used by developers - NO, because they are not efficient.

We've created extensions in our library's class EnumFlagsHelper like in SomeEnumHelperMethodsThatMakeDoingWhatYouWantEasier, but named the function as SetFlag instead of Include and ClearFlag instead of Remove.

In the body of SetFlag methods ( and in summary comment) I decided to add

Debug.Assert( false, " do not use the extension due to performance reason, use bitwise operation with the explanatory comment instead \n 
flags |= flag;// SetFlag")

and a similar message should be added to ClearFlag

Debug.Assert( false, " do not use the extension due to performance reason, use bitwise operation with the explanatory comment instead \n 
         flags &= ~flag; // ClearFlag  ")
like image 154
Michael Freidgeim Avatar answered Sep 28 '22 20:09

Michael Freidgeim


I've done something that works for me and that's very simple...

    public static T SetFlag<T>(this Enum value, T flag, bool set)
    {
        Type underlyingType = Enum.GetUnderlyingType(value.GetType());

        // note: AsInt mean: math integer vs enum (not the c# int type)
        dynamic valueAsInt = Convert.ChangeType(value, underlyingType);
        dynamic flagAsInt = Convert.ChangeType(flag, underlyingType);
        if (set)
        {
            valueAsInt |= flagAsInt;
        }
        else
        {
            valueAsInt &= ~flagAsInt;
        }

        return (T)valueAsInt;
    }

Usage:

    var fa = FileAttributes.Normal;
    fa = fa.SetFlag(FileAttributes.Hidden, true);
like image 36
Eric Ouellet Avatar answered Sep 28 '22 21:09

Eric Ouellet