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));
}
}
}
}
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:
Flags
enum there will be not much more than 32/64 correspondingly for int
/long
enums) values including common combination of flags like)0xFFFFFFFF
) - any value can be represented, otherwise again (value & ~ allFilgesOrTogether) == 0
will give you answer.