Printing Flags Enum as Separate Flags
I have a flags enum defined like this:
[Flags]
public enum MyEnum
{
None = 0x00,
Choice1 = 0x01,
Choice2 = 0x02,
Choice3 = 0x04,
Default = Choice1 | Choice2,
All = Default | Choice3
}
I would like a way to print out which flags are included in MyEnum.Default
. In this case, I'd want the output to be something like "Choice1, Choice2".
The problem with simp开发者_开发百科ly printing MyEnum.Default.ToString()
is that the output would be "Default" when I want "Choice1, Choice2".
Here's one option, but if I used this I'd have to update the printing every time I changed the enum.
((StudyData.Choice1 & StudyData.Default) == StudyData.Choice1 ? StudyData.Choice1.ToString() : "") + ", " +
((StudyData.Choice2 & StudyData.Default) == StudyData.Choice2 ? StudyData.Choice2.ToString() : "") + ", " +
((StudyData.Choice3 & StudyData.Default) == StudyData.Choice3 ? StudyData.Choice3.ToString() : "")
Does anyone have a cleaner way of doing this? Ideally, I'd like a way of printing out the flags included in MyEnum.Default
without having to change the printing code every time I added a new flag or changed the default.
Thanks!
Using the extension methods I've written here on a related question, this should be simple:
var value = MyEnum.Default;
var str = String.Join(", ", value.GetIndividualFlags());
// "Choice1, Choice2"
And here's the extension methods:
static class EnumExtensions
{
public static IEnumerable<Enum> GetFlags(this Enum value)
{
return GetFlags(value, Enum.GetValues(value.GetType()).Cast<Enum>().ToArray());
}
public static IEnumerable<Enum> GetIndividualFlags(this Enum value)
{
return GetFlags(value, GetFlagValues(value.GetType()).ToArray());
}
private static IEnumerable<Enum> GetFlags(Enum value, Enum[] values)
{
ulong bits = Convert.ToUInt64(value);
List<Enum> results = new List<Enum>();
for (int i = values.Length - 1; i >= 0; i--)
{
ulong mask = Convert.ToUInt64(values[i]);
if (i == 0 && mask == 0L)
break;
if ((bits & mask) == mask)
{
results.Add(values[i]);
bits -= mask;
}
}
if (bits != 0L)
return Enumerable.Empty<Enum>();
if (Convert.ToUInt64(value) != 0L)
return results.Reverse<Enum>();
if (bits == Convert.ToUInt64(value) && values.Length > 0 && Convert.ToUInt64(values[0]) == 0L)
return values.Take(1);
return Enumerable.Empty<Enum>();
}
private static IEnumerable<Enum> GetFlagValues(Type enumType)
{
ulong flag = 0x1;
foreach (var value in Enum.GetValues(enumType).Cast<Enum>())
{
ulong bits = Convert.ToUInt64(value);
if (bits == 0L)
//yield return value;
continue; // skip the zero value
while (flag < bits) flag <<= 1;
if (flag == bits)
yield return value;
}
}
}
Decorate your enum with FlagsAttribute. It does pretty much exactly what you're after:
[Flags]
public enum FooNum
{
foo = 0,
bar = 1,
lulz = 2,
borkbork = 4
}
FooNum f = FooNum.bar | FooNum.borkbork;
Debug.WriteLine(f.ToString());
should give you:
bar, borkbork
Print by single linq statement:
var names = Enum.GetValues(typeof(MyEnum))
.Cast<MyEnum>()
.Where(a => (values & a) == a)
.Select(a => a.ToString())
.Aggregate((current, next) => current + ", " + next);
Updated version to print only explicitly defined values:
var values = MyEnum.All;
var allAttrs = Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>();
var names = allAttrs
// leave only explicitly defined and not zero values
.Where(attr => allAttrs.Count(a => a != 0 && (attr & a) == a) == 1)
.Where(a => (values & a) == a)
.Select(a=>a.ToString())
.Aggregate((current, next) => current + ", " + next);
Console.WriteLine(names); // Choice1, Choice2, Choice3
using System;
using System.Collections.Generic;
using System.Text;
namespace printStar
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Enter the value ");
int k = int.Parse(Console.ReadLine());
int n = k - 1;
int x = 2 * (k - 1) + 1;
for (int p = 0; p <= n; p++)
{
for (int j = k - 1; j >= 0; j--)
{
Console.Write(" ");
}
for (int i = 0; i <= (x - 2 * (k - 1)); i++)
{
if (i % 2 == 1)
{
Console.Write("*");
}
else
{
Console.Write(" ");
}
}
Console.WriteLine();
k--;
}
Console.ReadLine();
}
}
}
I solved this in the shortest, clearest code possible that I expect performs well, although there is boxing in a couple of places. Using your type as an example:
MyEnum e = MyEnum.Choice1 | MyEnum.Choice2;
string s = FlagsEnumToString<MyEnum>(e); // Returns "Choice1, Choice2"
This is how it's implemented:
const string Separator = ", ";
public static string FlagsEnumToString<T>(Enum e)
{
var str = new StringBuilder();
foreach (object i in Enum.GetValues(typeof(T)))
{
if (IsExactlyOneBitSet((int) i) &&
e.HasFlag((Enum) i))
{
str.Append((T) i + Separator);
}
}
if (str.Length > 0)
{
str.Length -= Separator.Length;
}
return str.ToString();
}
static bool IsExactlyOneBitSet(int i)
{
return i != 0 && (i & (i - 1)) == 0;
}
Some comments might come up and I'll address these first:
I need to call your method providing both type and variable?
Because this can't be done with a generic T
argument implicitly. T
can't be cast to Enum
for use with HasFlag
. No, also not using where T : struct, IConvertible
.
The
foreach
also usesobject
?
Yes, also to be able to cast. Only object
can be cast to the other types T
, int
, Enum
.
I think this can be optimized by casting to
int
inside the loop once with a temporary variable.
I think so, yes. This code was written like this for clarity. So yes do that and get rid of those HasFlag
calls if you like.
I think you still can use
Enum
as theforeach
variable and save on casting.
No, because you need a cast to T
and that can only be done from object
. There might be 'better' solutions but this is most certainly the shortest and clearest one.
Use flags.ToString("g");
See Enumeration Format Strings
精彩评论