Reputation: 51
Trying to get array of all possible flags from enum value, say 3 to array of {1, 2}.
I have an extension
internal static MyEnum[] GetFlags(this MyEnum modKey)
{
string[] splitStr = modKey.ToString().Split(new string[1] { ", " }, StringSplitOptions.RemoveEmptyEntries);
MyEnum[] flags = new MyEnum[splitStr.Length];
for (int i = 0; i < splitStr.Length; i++)
{
flags[i] = (MyEnum)Enum.Parse(typeof(MyEnum), splitStr[i]);
}
return flags;
}
...but it seems a bit wasteful for the purpose. Could this be done more effectively?
Upvotes: 0
Views: 3339
Reputation: 109080
Both answers don't do what (I think) is asked: get the elementary values from an enum value, not any composed values. One example where this may be useful is when one enum value must be used in a Contains
statement in LINQ to a SQL backend that doesn't support HasFlag
.
For this purpose I first created a method that returns elementary flags from an enum type:
public static class EnumUtil
{
public static IEnumerable<TEnum> GetFlags<TEnum>()
where TEnum : Enum
{
return Enum.GetValues(typeof(TEnum))
.Cast<TEnum>()
.Where(v =>
{
var x = Convert.ToInt64(v); // because enums can be Int64
return x != 0 && (x & (x - 1)) == 0;
// Checks whether x is a power of 2
// Example: when x = 16, the binary values are:
// x: 10000
// x-1: 01111
// x & (x-1): 00000
});
}
}
And then a method that returns elementary flags from an enum value:
public static IEnumerable<TEnum> GetFlags<TEnum>(this TEnum enumValue)
where TEnum : Enum
{
return GetFlags<TEnum>()
.Where(ev => enumValue.HasFlag(ev));
}
Taking this enum type:
[Flags]
public enum WeekDay
{
Monday = 1 << 0,
Tuesday = 1 << 1,
Wednesday = 1 << 2,
Thursday = 1 << 3,
Friday = 1 << 4,
Saturday = 1 << 5,
Sunday = 1 << 6,
BusinessDay = Monday | Tuesday | Wednesday | Thursday | Friday,
WeekendDay = Saturday | Sunday,
All = BusinessDay | WeekendDay
}
The statements (in Linqpad)...
string.Join(",", EnumUtil.GetFlags<WeekDay>()).Dump();
var t = WeekDay.Thursday | WeekDay.WeekendDay;
string.Join(",", t.GetFlags()).Dump();
t = WeekDay.All;
string.Join(",", t.GetFlags()).Dump();
...return this:
Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
Thursday,Saturday,Sunday
Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
Basic idea taken from this answer to my question on Code Review.
Upvotes: 3
Reputation: 4249
assuming your MyEnum has a Flags Attribute, to test if a flag is set the (standard?) way is to perform a binary & between your value and the flag you want to test: so something like this should work:
internal static MyEnum[] GetFlags(this MyEnum modKey)
{
List<MyEnum> flags = new List<MyEnum>();
foreach (var flag in Enum.GetValues(typeof(MyEnum)))
{
if (modKey & flag == flag)
flags.Add((MyEnum)flag);
}
return flags.ToArray();
}
if you use .Net 4 or later, you can use HasFlag
if (modKey.HasFlag((MyEnum)flag))
...
Upvotes: 3
Reputation: 14896
You can simply filter all possible values of the MyEnum
to the ones in modKey
:
internal static MyEnum[] GetFlags(this MyEnum modKey)
{
return Enum.GetValues(typeof(MyEnum))
.Cast<MyEnum>()
.Where(v => modKey.HasFlag(v))
.ToArray();
}
Edit
Based on the comment below, in case of combinations specified, the method should only return the combinations, not all flags set.
The solution is to loop through all flags set in the enum starting from the highest one. In each iteration, we have to add a flag to the result, and remove it from the iterated enum until it's empty:
internal static MyEnum[] GetFlags(this MyEnum modKey)
{
List<MyEnum> result = new List<MyEnum>();
while (modKey != 0)
{
var highestFlag = Enum.GetValues(typeof(MyEnum))
.Cast<MyEnum>()
.OrderByDescending(v => v)
.FirstOrDefault(v => modKey.HasFlag(v));
result.Add(highestFlag);
modKey ^= highestFlag;
}
return result.ToArray();
}
Upvotes: 6