4

EDIT:

Most people suggest that flag enums should always have values of powers of two. That may be best practice but I am not defining enums here, rather checking them and want to cover all possible scenarios within reason. The question is really about the proper way to implement the function named EnumUtilities.IsValueDefinedAndComposite<T>.

ORIGINAL POST:

Consider the following Enum:

[Flags]
public enum TestWithFlags { One = 1, Two = 2, }

Following is the result of Enum.IsDefined with various values casted as TestWithFlags.

Output:

(1). Defined: True:  One.
(2). Defined: True:  Two.
(3). Defined: False: 100.
(4). Defined: False: One, Two.
(5). Defined: ?????: One, Two.

What I can't figure out is how to determine is an enum value is composite. Please see the function EnumUtilities.IsValueDefinedAndComposite<T> in the code below.

Here is the full code for convenience.

using System;
using System.Collections.Generic;
using System.Linq;

namespace MyNamespace
{
    [Flags]
    public enum TestWithFlags { One = 1, Two = 2, }

    public static class Program
    {
        private static void Main (string [] args)
        {
            TestWithFlags value;

            value = TestWithFlags.One; // True.
            Console.WriteLine("(1). Defined: {0}:  {1}.", Enum.IsDefined(typeof(TestWithFlags), value), value.ToString());

            value = TestWithFlags.Two; // True.
            Console.WriteLine("(2). Defined: {0}:  {1}.", Enum.IsDefined(typeof(TestWithFlags), value), value.ToString());

            value = (TestWithFlags) 100; // False.
            Console.WriteLine("(3). Defined: {0}: {1}.", Enum.IsDefined(typeof(TestWithFlags), value), value.ToString());

            value = TestWithFlags.One | TestWithFlags.Two; // False.
            Console.WriteLine("(4). Defined: {0}: {1}.", Enum.IsDefined(typeof(TestWithFlags), value), value.ToString());

            value = TestWithFlags.One | TestWithFlags.Two; // Not implemented.
            Console.WriteLine("(5). Defined: N/A:   {1}.", EnumUtilities.IsValueDefinedAndComposite(value), value.ToString());

            Console.WriteLine();
            Console.Write("Press any key to continue...");
            Console.ReadKey(true);
        }
    }

    public static class EnumUtilities
    {
        public static List<T> GetValues<T> ()
            where T: struct, IComparable, IFormattable, IConvertible
        {
            EnumUtilities.ThrowOnNonEnum<T>();

            var list = Enum.GetValues(typeof(T)).OfType<T>().ToList().ConvertAll<T>(v => ((T) v));

            return (list);
        }

        public static bool IsValueDefinedAndComposite<T> (T value)
            where T: struct, IComparable, IFormattable, IConvertible
        {
            EnumUtilities.ThrowOnEnumWithoutFlags<T>();

            var values = EnumUtilities.GetValues<T>();

            var result = false;
            //var result = values.Count(v => (value | v) == value) > 1;
            // How to determine whether the argument [value] is composite.

            return (result);
        }

        public static bool IsValueDefinedAndNonComposite<T> (T value)
            where T: struct, IComparable, IFormattable, IConvertible
        {
            EnumUtilities.ThrowOnNonEnum<T>();

            return (Enum.IsDefined(typeof(T), value));
        }

        public static bool IsValueDefined<T> (T value)
            where T: struct, IComparable, IFormattable, IConvertible
        {
            EnumUtilities.ThrowOnNonEnum<T>();

            return (EnumUtilities.IsValueDefinedAndNonComposite(value) || EnumUtilities.IsValueDefinedAndComposite(value));
        }

        private static void ThrowOnNonEnum<T> ()
        {
            if (!typeof(T).IsEnum)
            {
                throw (new ArgumentException("The generic argument [<T>] must be an enumeration.", "T: " + typeof(T).FullName));
            }
        }

        private static void ThrowOnEnumWithFlags<T> ()
        {
            EnumUtilities.ThrowOnNonEnum<T>();

            var attributes = typeof(T).GetCustomAttributes(typeof(FlagsAttribute), false);

            if (attributes.Length > 0)
            {
                throw (new ArgumentException("The generic argument [<T>] must be an enumeration without the [FlagsAttribute] applied.", "T: " + typeof(T).FullName));
            }
        }

        private static void ThrowOnEnumWithoutFlags<T> ()
        {
            EnumUtilities.ThrowOnNonEnum<T>();

            var attributes = typeof(T).GetCustomAttributes(typeof(FlagsAttribute), false);

            if (attributes.Length == 0)
            {
                throw (new ArgumentException("The generic argument [<T>] must be an enumeration with the [FlagsAttribute] applied.", "T: " + typeof(T).FullName));
            }
        }
    }
}
Raheel Khan
  • 14,205
  • 13
  • 80
  • 168
  • 8
    For a properly composed flags enum, distinct values will all be powers of 2. – Preston Guillot Jun 28 '14 at 18:59
  • What should be result for 3 in case of `{One=1, Two=2, OneAndTwoMask=3}`? – Alexei Levenkov Jun 28 '14 at 19:14
  • @PrestonGuillot I don't think [Flags](http://msdn.microsoft.com/en-us/library/system.flagsattribute%28v=vs.110%29.aspx) imply all values are individual bits, rather intended usage as bit-field. I.e. often there are "helper" values like `ReadOrWrite` or masks `PermissionBitsMask` - so it would be interesting to see if muli-bit value of enum should be considered composite or not by OP. – Alexei Levenkov Jun 28 '14 at 19:19
  • @AviTurner - I'm not saying anything about whether it is good or bad design - just existing one so it may need to be taken into account when writing utility functions. Sample - [FileShare](http://msdn.microsoft.com/en-us/library/system.io.fileshare%28v=vs.110%29.aspx) – Alexei Levenkov Jun 28 '14 at 19:23
  • 3
    @AlexeiLevenkov By "distinct" I meant, "those that are not composite" e.g. `Read` and `Write`, but *not* `ReadOrWrite`. If the OP means something *else* by "composite", I'm not convinced `Flags` is what he's looking for. – Preston Guillot Jun 28 '14 at 19:34

7 Answers7

4

You can try something like (Not Tested!):

   public static bool IsValueDefinedAndComposite<T>(T value)
        where T : struct, IComparable, IFormattable, IConvertible
    {
        EnumUtilities.ThrowOnEnumWithoutFlags<T>();

        var values = EnumUtilities.GetValues<T>();


        var result = values.OfType<T>().Contains(value);
        //var result = values.Count(v => (value | v) == value) > 1;
        // How to determine whether the argument [value] is composite.

        return (result);
    }

Basically, it is just checking if the value argument is part of the values, and if it is not, it is a composite.

Avi Turner
  • 10,234
  • 7
  • 48
  • 75
2

To answer your question; you can check to see if flag value is a singular value, or a composite of many flags, by checking to see if is a power of two.

See How to check if a number is a power of 2

bool IsPowerOfTwo(ulong x)
{
     return (x != 0) && ((x & (x - 1)) == 0);
}

If it isn't, then it has many set flags (as each flag must be a power of two).

Community
  • 1
  • 1
Meirion Hughes
  • 24,994
  • 12
  • 71
  • 122
  • This does not really answer the question. See @AlexeiLevenkov comment for details. – Avi Turner Jun 28 '14 at 19:31
  • I read over it again. I still think if a compiled (known) enum value resolves to a non-power of two value, then it is composite. Whether it is a valid composite would then be to check that each bit resolves to a known compiled enum value. – Meirion Hughes Jun 28 '14 at 20:20
  • Please note that the test is being done in a `EnumUtilities` class. As @AlexeiLevenkov has commented since it is a utility class rather than referencing a compilation-time-known Enum set `it may need to be taken into account when writing utility functions` – Avi Turner Jun 29 '14 at 06:16
1

Note that if enums in question are strictly bit flags {One = 1<<0, Two = 1<< 1, ALot=1<<20} than other answers checking for "single bit set" would be more appropriate. Look into this one if your enums can contains masks with multiple bits. I.e. some imaginary custom "float" number masks present as Flags enum { Sign = 0x80, Mantissa=0x78, Power = 0x7}.


To test if value can be represented by some combination of values in the enum:

One-time: just start dropping bit for each value till you either run out values or got 0 as result. Pseudo-code (important part is & ~enumValue - AND with negated value)

var remainingBits = value; 
foreach (var enumValue in GetAllValuesOfEnum(....))
{
  if (value == enumValue) return "ExisitngNonComposite";
  var remainingBits = current & ~enumValue;
  if (remainingBits == 0) return "Composite";
}
return "CanNotBeRepresented";    

If you need to repeat it many times and just interested if value can be represented:

  1. get all values of enum
  2. OR them together (for reasonable Flags enum there will be not much more than 32/64 correspondingly for int/long enums) values including common combination of flags like)
  3. if got all ones (0xFFFFFFFF) - any value can be represented, otherwise again (value & ~ allFilgesOrTogether) == 0 will give you answer.
Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
  • I think you could exploit this a little better as any composite will set > 1 sig dig... So I'm pretty sure you could just check the bits for >= 2 set as 1. – Haney Jun 28 '14 at 19:51
  • 2
    @DaveHaney it looks like OP writes generic utility function - so enum with elements set to multiple bits (i.e. masks rather than individual bits) may need to be handled. I.e. some imaginary custom "float" number masks present as Flags enum `{ Sign = 0x80, Mantissa=0x78, Power = 0x7}`. – Alexei Levenkov Jun 28 '14 at 19:56
1

This probably is actually fairly easy to solve if you approach it in a slightly different manner: convert the enum to a string using the F enum string format, then check to see if the resultant string contains a comma.

From the Microsoft documentation:

Displays the enumeration entry as a string value, if possible. If the value can be completely displayed as a summation of the entries in the enumeration (even if the Flags attribute is not present), the string values of each valid entry are concatenated together, separated by commas. If the value cannot be completely determined by the enumeration entries, then the value is formatted as the integer value. The following example illustrates the F format specifier.

This will work on all enums, whether they have the flags attribute defined or not, so in the code update below, I have changed the initial value test to just ThrowOnNonEnum.

Here is the implementation of your method using this approach:

    public static bool IsValueDefinedAndComposite<T>(T value)
        where T : struct, IComparable, IFormattable, IConvertible
    {
        EnumUtilities.ThrowOnNonEnum<T>();

        var valueAsString = Enum.Format(typeof (T), value, "F");

        // If the value contains a comma, then it is defined and composite
        if (valueAsString.Contains(","))
        {
            return true;
        }
        else
        {
            // If the value cannot be completely determined by the enumeration entries, it will be numeric. 
            // This is one possible method for testing this.
            double valueAsDouble = 0;
            return !(Double.TryParse(valueAsString, out valueAsDouble));
        }
    }

And here is an updated version of your test item 5 along with a new item 6 that validates the not-completely-defined scenario:

 value = TestWithFlags.One | TestWithFlags.Two; // True
 Console.WriteLine("(5). Defined: {0}: {1}.", EnumUtilities.IsValueDefinedAndComposite(value), value.ToString());

 value = (TestWithFlags)6; // False
 Console.WriteLine("(6). Defined: {0}: {1}.", EnumUtilities.IsValueDefinedAndComposite(value), value.ToString());

And the output:

(1). Defined: True:  One.
(2). Defined: True:  Two.
(3). Defined: False: 100.
(4). Defined: False: One, Two.
(5). Defined: True: One, Two.
(6). Defined: False: 6.
competent_tech
  • 44,465
  • 11
  • 90
  • 113
0

You should set the enum values as power of two.

Than you can test it by doing (TestWithFlags) 2^NUMBER to test if a number is enum flag.

Orel Eraki
  • 11,940
  • 3
  • 28
  • 36
0

If I understand your question, then you need to check if a composite (flags) enum contains a specific value? Did you try to use bitwise operations?

if( 0 != (myCompositeEnum & TestWithFlags.One) )
{
    // then TestWithFalgs.One is in the composite.
}

BTW, your Enum values should be a power of two.
This is a StackOverFlow post on a similar question.

Community
  • 1
  • 1
m1o2
  • 1,549
  • 2
  • 17
  • 27
0

The situation can be somewhat more complex that just testing that the incoming value is not a single power of two, because defined enum flag values can themselves be composites of other defined enum values, for instance:

[Flags]
public enum TestWithFlags
{
    One = 1,
    Two = 2,
    Four = 4,
    Seven = Four | Two | One,
    Eight = 8,
    Nine = Eight | One,
}

So, what does "composite" mean in this case? Does it mean "a mask of two or more valid enum values", or does it mean "not a defined enum value but instead a a mask of two or more defined enum values"?

Assuming you want "not a defined enum value but instead a a mask of two or more defined enum values", the following should work:

public static class EnumUtilities
{
    public static List<T> GetValues<T> ()
        where T: struct, IComparable, IFormattable, IConvertible
    {
        EnumUtilities.ThrowOnNonEnum<T>();

        var list = Enum.GetValues(typeof(T)).OfType<T>().ToList().ConvertAll<T>(v => ((T) v));

        return (list);
    }

    private static ulong[] GetValuesAsUint64<T>()
        where T : struct, IComparable, IFormattable, IConvertible
    {
        EnumUtilities.ThrowOnNonEnum<T>();
        IList eList = Enum.GetValues(typeof(T));
        ulong[] list = new ulong[eList.Count];
        for (int i = 0; i < eList.Count; i++)
        {
            list[i] = Convert.ToUInt64(eList[i]);
        }
        return list;
    }

    public static bool IsValueDefinedOrComposite<T>(T value)
        where T : struct, IComparable, IFormattable, IConvertible
    {
        EnumUtilities.ThrowOnEnumWithoutFlags<T>();

        var intValue = Convert.ToUInt64(value);
        var intValues = GetValuesAsUint64<T>();

        if (intValue == 0)
        {
            return intValues.Contains(intValue);
        }
        else
        {
            int matches = 0;

            foreach (var test in intValues)
            {
                if ((test & intValue) == test)
                {
                    matches++;
                    intValue &= ~(test);
                }
            }

            return matches > 0 && intValue == 0;
        }
    }

    public static bool IsValueDefinedAndNonComposite<T> (T value)
        where T: struct, IComparable, IFormattable, IConvertible
    {
        EnumUtilities.ThrowOnNonEnum<T>();

        return (Enum.IsDefined(typeof(T), value));
    }

    public static bool IsValueDefinedAndComposite<T>(T value)
        where T : struct, IComparable, IFormattable, IConvertible
    {
        return IsValueDefinedOrComposite(value) && !IsValueDefinedAndNonComposite(value);
    }

    private static void ThrowOnNonEnum<T> ()
    {
        if (!typeof(T).IsEnum)
        {
            throw (new ArgumentException("The generic argument [<T>] must be an enumeration.", "T: " + typeof(T).FullName));
        }
    }

    private static void ThrowOnEnumWithFlags<T> ()
    {
        EnumUtilities.ThrowOnNonEnum<T>();

        var attributes = typeof(T).GetCustomAttributes(typeof(FlagsAttribute), false);

        if (attributes.Length > 0)
        {
            throw (new ArgumentException("The generic argument [<T>] must be an enumeration without the [FlagsAttribute] applied.", "T: " + typeof(T).FullName));
        }
    }

    private static void ThrowOnEnumWithoutFlags<T> ()
    {
        EnumUtilities.ThrowOnNonEnum<T>();

        var attributes = typeof(T).GetCustomAttributes(typeof(FlagsAttribute), false);

        if (attributes.Length == 0)
        {
            throw (new ArgumentException("The generic argument [<T>] must be an enumeration with the [FlagsAttribute] applied.", "T: " + typeof(T).FullName));
        }
    }
}  

Note that enums can range from being byte-sized to (u)long-sized; each should be checked (I haven't - I only did some simple testing.). And I feel as though there ought to be a nicer way to do this, but the enum utilities haven't changed since c# 1.0 and so are very primitive. Flags with a zero value also need to be tested.

Finally, I found it oddly awkward to convert the generic enum to a type that supports bitmasking; I used this suggestion.

Community
  • 1
  • 1
dbc
  • 104,963
  • 20
  • 228
  • 340
  • Is there a way to check if a value is a direct assignment (i.e. not `Nine = Eight | One`)? It's a `Flags` enum, so I'm basically looking for a nicer substitute of `enumValue.ToString().Contains(',')`. – Shimmy Weitzhandler May 24 '17 at 15:48