14

I came up with this piece of code that converts the set flags in a variable of type Flag Enumeration and returns the set flags as integers. I'd like to know if this is the best approach.

Example enumeration:

[Flags]
enum Status {
  None = 0x0,
  Active = 0x1,
  Inactive = 0x2,
  Canceled = 0x4,
  Suspended = 0x8
}

The extension method that converts the set flags to array of int that I came up with:

public static class Extensions
{
    public static int[] ToIntArray(this System.Enum o) 
    {
        return o.ToString()
            .Split(new string[] { ", " }, StringSplitOptions.None)
            .Select(i => (int)Enum.Parse(o.GetType(), i))
            .ToArray();
    }
}

This is how I use it:

Status filterStatus = Status.Suspended | Status.Canceled;

int[] filterFlags = filterStatus.toIntArray();

foreach (int flag in filterFlags) {
   Console.WriteLine("{0}\n", flag);
}

It will output:

4
8

As you can see, to get this done I'm doing the following:

  1. Converting the variable to string. It outputs something like: Suspended, Canceled
  2. Splitting that string into an array of strings: { "Suspended", "Canceled" }
  3. Converting that string to the enumeration value with Enum.Parse.
  4. Casting the value to an integer.
  5. Converting the IEnumerable to int[].

It works, but I just don't think it's the best approach. Any suggestions to improve this bit of code?

Cameri
  • 143
  • 1
  • 1
  • 6
  • 1
    Not all `enum` types are based on `int` as the underlying type, so the cast could fail. – Ben Voigt Jul 08 '10 at 22:11
  • @Ben - That's a good point, and that would affect my solution in the answer. The best idea there is probably to realize this isn't a general solution, but a specific one to apply when you know something about the enum being used. You could go wild with a generic the underlying type, but I think that would be overkill in most cases. – Steve Mitcham Jul 08 '10 at 22:27
  • It is indeed not a general solution, and it's only specific for enumerations with the underlying type being System.Int32. Also, all values in the enumeration are independent, thus Steve Mitcham's solution work, while keeping it LINQ-like. – Cameri Jul 09 '10 at 14:09

3 Answers3

15

To keep it linq-like

var flags = Enum.GetValues(typeof(Status))
                .Cast<int>()
                .Where(f=> f & o == f)
                .ToList();

One gotcha with this approach is that it will include aggregate enumeration values. For example:

[Flags]
public enum Status
{
    None = 0,
    One = 1,
    Two = 2,
    All = One | Two,
}

var flags = Enum.GetValues(typeof(Status))
                .Cast<int>()
                .Where(f=> f & o == f)
                .ToList();

Here flags will have 1, 2, 3, not just 1, 2.

CodeNaked
  • 40,753
  • 6
  • 122
  • 148
Steve Mitcham
  • 5,268
  • 1
  • 28
  • 56
  • Should be `Where(f => f&o == f)` – Ben Voigt Jul 09 '10 at 00:10
  • @LukeH: The result of `f & o` is not restricted to `f` and `0`. Consider for example the `System.Windows.Forms.Keys` enum. – Ben Voigt Jul 09 '10 at 02:32
  • @Ben: You're right. And I also concede your points about the casts from `Enum` to `ulong`. I've deleted my misleading comments. (It was very late here when I posted and I obviously wasn't thinking straight!) – LukeH Jul 09 '10 at 09:58
3

Just cast to an int, and then print out individual bits.

int x = (int)o
List<int> flags = new List<int>();
for(int i = 1; i < (1 << 30); i <<= 1)
    if(x & i != 0)
        flags.Add(i);
return flags;
Anon.
  • 58,739
  • 8
  • 81
  • 86
0

Well, you can replace steps 1-3 with a call to Enum.GetValues. And then use the bitwise & operator to test which of those are set in your value. This will be much faster than working with strings.

This method will support flags which are more than a single bit wide. If all bits are independent, just do what Anon said.

Community
  • 1
  • 1
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720