1

I have multiple flags enums defined in code similar to the following

[Flags]
public enum Colors
{
   None = 0,
   Red = 1,
   Green = 2,
   Blue = 4, 
   Purple = Red | Blue,
   Brown = Red | Green,
}

The following code produces the following output

Colors color1 = Colors.Red | Colors.Blue;
Colors color2 = Colors.Purple;
string s1 = color1.ToString(); // Sets s1 to "Purple"
string s2 = color2.ToString(); // Sets s2 to "Purple"



I want a method that outputs the individual bits of a bitwise enum, even if a matching combination is defined.

private void Foo()
{
  Colors color1 = Colors.Red | Colors.Blue;
  Colors color2 = Colors.Purple;
  string s1 = CreateColumnString(color1); // Sets s1 to "Red|Blue"
  string s2 = CreateColumnString(color2); // Sets s2 to "Red|Blue"
}

I thought I could loop through all the values of an enum and check if the value is a power of two. But I can't figure out how to get the underlying value of the Enum argument.

private string CreateColumnString(object value)
{
 //is this an enum with Flags attribute?
 if (value is Enum  && value.GetType().GetCustomAttributes(typeof(FlagsAttribute), true).Length > 0)
 {
    Enum e = (Enum)value;
    //Get a list of Enum values set in this flags enum
    IEnumerable<Enum> setValues = 
      Enum.GetValues(value.GetType())
          .Cast<Enum>()
          .Where(eachEnum => IsPowerOfTwo(eachEnum) && value.HasFlag(eachEnum)); 

    return string.Join("|", setValues);
 }
 else
 {
    return value != null ? value.ToString() : string.Empty;
 }
 return str;
}

private static bool IsPowerOfTwo(Enum e)
{
   int x = (int)e; //ERROR cannot convert type 'System.Enum' to 'ulong'
   return (x != 0) && ((x & (x - 1)) == 0);
}
Osiris
  • 489
  • 3
  • 14

4 Answers4

1

There may be better ways of doing this, but this should do what you are looking for:

private static string AsString<T>(this T values)
{
    Enum v = (Enum)Convert.ChangeType(values, typeof(Enum));
    Array array = Enum.GetValues(typeof(T));
    IEnumerable<Enum> setFlags = array
        .Cast<Enum>()
        .Where(c => v.HasFlag(c) && IsDistinctValue(c));

    return values.Equals(default(T))
        ? default(T).ToString()
        : string.Join("|", setFlags.Where(c => Convert.ToInt32(c) != 0).Select(c => c.ToString()));
}

private static bool IsDistinctValue(Enum value)
{
    int current = Convert.ToInt32(value) >> 1;
    while (current > 0)
    {
        if ((Convert.ToInt32(value) & current) != 0)
        {
            return false;
        }
        current >>= 1;
    }

    return true;
}

It will essentially list the values for the set flags, except those that "contain" other flags. It figures this out by taking the value that is being tested, decrementing it towards zero and check whether the original value has that decremented value set as a flag. Finally, it will remove the "None" value, unless no flag is set.

Use it like so:

Colors c = Colors.Purple;
Console.WriteLine(c.AsString());
Fredrik Mörk
  • 155,851
  • 29
  • 291
  • 343
  • Thanks, but the Colors enum is just an example. I'd like a method where I can pass any flags Enum. – Osiris Dec 11 '12 at 23:08
  • @Osiris there, updated the code to be more generic. You may want to add some of those checks to verify that it is indeed a flags enum coming in as well. – Fredrik Mörk Dec 11 '12 at 23:24
  • @Osiris couldn't let it go, so I came back, found a bug and made it better. Now it should handle most scenarios (also Enums that are not decorated with the Flags attribute). – Fredrik Mörk Dec 12 '12 at 12:08
0

You can use the HasFlag method:

.Where(e.HasFlag)

However, I think that your Enum.GetValues call will also get the multi-bit values that your enum type names.

EDIT:

Here's another approach that you might get to work:

if (Enum.GetUnderlyingType(e.GetType()) != typeof(int))
  throw new NotImplementedException();

var list = new List<Enum>();
for (int i = 1; i != 0; i <<= 1)
{
  var eachEnum = (Enum)(Enum.ToObject(e.GetType(), i));
  if (e.HasFlag(eachEnum))
    list.Add(eachEnum);
}

return string.Join(" | ", list);
Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181
  • Thanks, the HasFlag seems to work. Now I just need to verify the current value is a power of two. – Osiris Dec 11 '12 at 22:56
0

Here's another approach. I figure the more options you have, the better :)

public static class EnumHelpers
{
    public static string ToStringExtended<T>(this Enum e)
    {
        if (!(e.GetType().GetCustomAttributes(typeof(FlagsAttribute), true).Length > 0))
            return e.ToString();

        List<string> eNames = new List<string>();
        foreach (T fish in Enum.GetValues(typeof(T)))
        {
            Enum num = fish as Enum;
            if (e.HasFlag(num) && Convert.ToInt32(fish) != 0 && Convert.ToInt32(fish) != Convert.ToInt32(e))
                eNames.Add(fish.ToString());
        }

        return eNames.Count > 1 ? String.Join("|", eNames.ToArray()) : e.ToString();
    }
}

The usage is almost identical to what Fredirk proposed:

Colors c = Colors.Purple;
Console.WriteLine(c.ToStringExtended<Colors>());
// Output : Red|Blue
Ichabod Clay
  • 1,981
  • 13
  • 17
0

An answer in 4 lines of code not counting the method signature.

    private static string CreateColumnString(Colors value)
    {
        // This is how we do it in embedded programming in C
        // In C we wouldn't need to convert, but alas in C# we do
        // So first ... 
        var num = Convert.ToByte(value);  
        // ToUint16 would work as well, but ToByte makes your intentions clearer

        // Then bitwise '& 'every bit position you care about to compose your string
        // For example: 0b0011 & 0b1111 = 0b0011    (3 AND 15 = 3)
        var s = (num & 1) > 0 ? "Red"   : "";
        s = (num & 2) > 0 ? s + "|Green": s;
        return (num & 2) > 0 ? s + "|Blue" : s;
    }