Brian
Brian

Reputation: 1231

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 simply 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!

Upvotes: 10

Views: 13266

Answers (6)

Pete M
Pete M

Reputation: 2048

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

Upvotes: 13

user626528
user626528

Reputation: 14417

Use flags.ToString("g");
See Enumeration Format Strings

Upvotes: 3

SergeyA
SergeyA

Reputation: 4487

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

Upvotes: 2

Kay Zed
Kay Zed

Reputation: 1378

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 uses object?

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 the foreach 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.

Upvotes: 1

SANJU
SANJU

Reputation: 11

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();
        }
    }
}

Upvotes: 1

Jeff Mercado
Jeff Mercado

Reputation: 134611

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;
        }
    }
}

Upvotes: 18

Related Questions