tags:

views:

1005

answers:

8

Consider this:

[Flags]
enum Colors
{
    Red=1,
    Green=2,
    Blue=4
}

Colors myColor=Colors.Red|Colors.Blue;

Currently, I'm doing it as follows:

int length=myColors.ToString().Split(new char[]{','}).Length;

But I hope there is a more efficient way of finding the length, maybe based on bitset operations.

Please, if possible, provide explanation why and how your solution works.

Also, if this a duplicate, please point to it and I'll delete this question. The only similar questions on SO I've been able to find were concerned about finding the length of all possible combinations of Colors enum, but not of the myColors variable.

UPDATE: I carefully benchmarked every solution (1 000 000 iterations each) and here is the results:

  1. Stevo3000 - 8ms
  2. MattEvans - 10ms
  3. Silky - 34ms
  4. Luke - 1757ms
  5. Guffa - 4226ms
  6. Tomas Levesque - 32810ms

The Stevo3000 is a clear winner (with Matt Evans holding silver medal).

Thank you very much for your help.

UPDATE 2: This solution runs even faster: 41 ms for 100 000 000 iterations (roughly 40 times faster (32bit OS) than Stevo3000)

UInt32 v = (UInt32)co;
v = v - ((v >> 1) & 0x55555555); 
v = (v & 0x33333333) + ((v >> 2) & 0x33333333); 
UInt32 count = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
+1  A: 

Assuming they are flags, you can just use one of the methods here, to count the number of bits set.

It works because, as long as they are flags, when each one is 'OR'd' on, it sets one bit.

-- Edit

Sample code using one of the methods on that link:

[Flags]
enum Test
{
 F1 = 1,
 F2 = 2,
 F3 = 4
}


class Program
{
 static void Main(string[] args)
 {
  int v = (int) (Test.F1 | Test.F2 | Test.F3); // count bits set in this (32-bit value)
  int c = 0; // store the total here
  int[] S = {1, 2, 4, 8, 16}; // Magic Binary Numbers
  int[] B = {0x55555555, 0x33333333, 0x0F0F0F0F, 0x00FF00FF, 0x0000FFFF};

  c = v - ((v >> 1) & B[0]);
  c = ((c >> S[1]) & B[1]) + (c & B[1]);
  c = ((c >> S[2]) + c) & B[2];
  c = ((c >> S[3]) + c) & B[3];
  c = ((c >> S[4]) + c) & B[4];

  Console.WriteLine(c);
  Console.Read();
 }
}
Noon Silk
+1  A: 

A rough approximation will be just counting the number of bits set in myColors, but that will only work if every enumeration members' value is power of 2.

Anton Gogolev
A: 

Try this...

Colors.GetValues().Length();

...or is that too obvious?

EDIT:

OK, I just read the question again, and realised that you need the length of 'mycolors', not 'Colors' - let me think about that.

FURTHER EDIT:

Now I'm confused - the OP's posted solution would never work, as myColor.ToString() returns '5' and applying Split(new char[]{','}) to this would result in a array with a length of 1. Did the OP actually get this to work?

belugabob
It's not obvious because it's wrong! The OP is trying to determine how many flags are set in an enum variable, not how many are defined in the enum itself.
LukeH
I believe your code is a mistake, sorry.What you meant was: Enum.GetValues(typeof(Colors)).Length;But this calculates the length of Colors, not myColors. Please read the question.
Valentin Vasiliev
belugabob: He's counting the number of commas. When there is more than one item in the value (a | b), it'll have two commas. It's a very bad way of doing the counting, hence the reason he's asked his question :)
Noon Silk
@silky: btw, my method is correct, but a bit slow. I'm counting array length after splitting the string by comma. There is no algorithmic mistakes, just a bunch of temporary strings ;-)
Valentin Vasiliev
I agree that it is correct, but not a good approach, as I'm sure you know :)
Noon Silk
@further edit: no, it returns "Red, Blue", not "5"
Valentin Vasiliev
@Valentin: It was returning '5', because I accidentally omitted the [Flags] attribute - I obviously need more coffee this morining. :-)
belugabob
+2  A: 

Here's a reasonably easy way of counting the bits. Each bit is shifted in-turn to the LSB of an Int64 which is AND-ed with 1 (to mask out any of the other bits) and then added to the running total.

int length = Enumerable.Range(0, 64).Sum(x => ((long)myColor >> x) & 1);
LukeH
It's not more difficult; keep shifting until you hit 0, assuming a shift doesn't carry the MSB.
strager
+1, short and efficient (although not very readable...)
Thomas Levesque
@strager: I'm not sure what you mean by "until you hit 0". Can you elaborate?
LukeH
@Luke - See my example, not too hard to handle a number of any width.
Stevo3000
@Luke, Something like: int tmp = myColor; while(tmp != 0) { tmp >>= 1; ++bitsSet; }. This has issues with signed numbers though, as I mentioned.
strager
@Luke - I'm not fully up to speed on lambda functions, but it looks like you will iterate 64 times?
Stevo3000
@Stevo3000: Yep, it iterates 64 times. Once for each bit.
LukeH
@Luke - That's pretty excesive! If you look at my answer it only iterates once for each bit set. I do like the way you can do so much with a single line, but this is one of those cases where it is inefficient.
Stevo3000
@Stevo3000: I said this technique was easy, not efficient! Using LINQ for this kind of thing will never deliver the fastest solution: The one-liner above is actually instantiating several iterator objects and making dozens of method calls behind-the-scenes, compared to your answer which only requires a plain loop and some integer/bitwise arithmetic.
LukeH
@Luke - I wasn't doubting that it was easy, but the OP had asked for efficiency in the question. Good to see different approaches though.
Stevo3000
+5  A: 

The following code will give you the number of bits that are set for a given number of any type varying in size from byte up to long.

public static int GetSetBitCount(long lValue)
{
  int iCount = 0;

  //Loop the value while there are still bits
  while (lValue != 0)
  {
    //Remove the end bit
    lValue = lValue & (lValue - 1);

    //Increment the count
    iCount++;
  }

  //Return the count
  return iCount;
}

This code is very efficient as it only iterates once for each bit rather than once for every possible bit as in the other examples.

Stevo3000
BTW: assumes that each flag sets a single bit and no flags overlap.
devstuff
@Luke - Couldn't say it better myself.
Stevo3000
@devstuff - @Luke's comment appears to have been removed. A valid flag is either a bit or a composite made up of several bits (used as shorthand). This function is only for calculating the indervidual bits.
Stevo3000
+2  A: 

Here's my take on this... it counts the number of set bits in the value

int val = (int)myColor;
int count = 0;

while (val > 0)
{
    if((val & 1) != 0)
    {
        count++;
    }

    val = val >> 1;
}
Matt
@Matt - It looks like this works, but it looks like it needs more iterations than there are bits set, causing you to need an if block.
Stevo3000
@Stevo3000 - Yeah, you're right it'll iterate all the bits up to the most significant set bit. Your solution doesn't, nice one! :) I'll definitely remember that for the future
Matt
A: 

Here are a few extension methods to manipulate Flags enumerations :

public static class EnumExtensions
{
    private static void CheckEnumWithFlags<T>()
    {
        if (!typeof(T).IsEnum)
            throw new ArgumentException(string.Format("Type '{0}' is not an enum", typeof(T).FullName));
        if (!Attribute.IsDefined(typeof(T), typeof(FlagsAttribute)))
            throw new ArgumentException(string.Format("Type '{0}' doesn't have the 'Flags' attribute", typeof(T).FullName));
    }

    public static bool IsFlagSet<T>(this T value, T flag) where T : struct
    {
        CheckEnumWithFlags<T>();
        long lValue = Convert.ToInt64(value);
        long lFlag = Convert.ToInt64(flag);
        return (lValue & lFlag) != 0;
    }

    public static IEnumerable<T> GetFlags<T>(this T value) where T : struct
    {
        CheckEnumWithFlags<T>();
        foreach (T flag in Enum.GetValues(typeof(T)).Cast<T>())
        {
            if (value.IsFlagSet(flag))
                yield return flag;
        }
    }

    public static T SetFlags<T>(this T value, T flags, bool on) where T : struct
    {
        CheckEnumWithFlags<T>();
        long lValue = Convert.ToInt64(value);
        long lFlag = Convert.ToInt64(flags);
        if (on)
        {
            lValue |= lFlag;
        }
        else
        {
            lValue &= (~lFlag);
        }
        return (T)Enum.ToObject(typeof(T), lValue);
    }

    public static T SetFlags<T>(this T value, T flags) where T : struct
    {
        return value.SetFlags(flags, true);
    }

    public static T ClearFlags<T>(this T value, T flags) where T : struct
    {
        return value.SetFlags(flags, false);
    }

    public static T CombineFlags<T>(this IEnumerable<T> flags) where T : struct
    {
        CheckEnumWithFlags<T>();
        long lValue = 0;
        foreach (T flag in flags)
        {
            long lFlag = Convert.ToInt64(flag);
            lValue |= lFlag;
        }
        return (T)Enum.ToObject(typeof(T), lValue);
    }
}

In your case you can use the GetFlags method :

int count = myColors.GetFlags().Count();

It's probably not as efficient as Luke's answer, but it's easier to use...

Thomas Levesque
A: 

The solution that is most reliable is to test for each value in the enumeration:

int len = 0;
foreach (Colors color in Enum.GetValues(typeof(Colors))) {
   if ((myColor & color) == color) {
      len++;
   }
}

This will work even if the value has bits set where there are no defined value in the enumeration, for example:

Colors myColor = (Colors)65535;

This will also work for enumerations with values that use more than a single bit:

[Flags]
enum Colors {
   Red = 0xFF0000,
   Green = 0x00FF00,
   Blue = 0x0000FF
}
Guffa
BTW: if using this in a loop it will run much quicker if the reflected Enum.GetValues() result is retrieved from a static variable.
devstuff