views:

236

answers:

2

Enums can sure be confusing. I am trying to create an extension method on the Enum type that will take a value and return the names of all the bits that match.

Given:

[Flags]
public enum PlanetsEnum
{
  Mercury=1,
  Venus=2,
  Earth=4,
  Mars=8,
  Jupiter=16,
  //etc....
}

I would like to create an extension method that return a dictionary of only the selected values. So if:

PlanetsEnum neighbors = PlanetsEnum.Mars | PlanetEnum.Venus; //10

IDictionary dict = neighbors.ToDictionary();

foreach (KeyValuePair<String, Int32> kvp in dict)
{
  Console.WriteLine(kvp.Key);
}

/*
*  Should Print:
*      Mars
*      Venus
*/

I would expect to see Mars & Venus written to the console but I am instead seeing all the values of PlanetEnum. Here is my extension method code:

public static IDictionary<string, Int32> ToDictionary(this Enum enumeration)
{
  Type type = enumeration.GetType();
  return Enum.GetValues(type).Cast&lt;Int32>().ToDictionary(field => Enum.GetName(type, field));
}

Does anyone see what I am doing wrong? I know that Enum.GetValues is returning all fields of the enum type, how do I get it to only return fields of the enum instance?

Thanks a lot for any help,

Keith

+2  A: 

Try the following. It only works for Enum's which are int32's

public static IDictionary<String, Int32> ToDictionary(this Enum enumeration)
{
    var map = new Dictionary<string, int>();
    var value = (int)(object)enumeration;
    var type = enumeration.GetType();
    foreach (int cur in Enum.GetValues(type))
    {
        if (0 != (cur & value))
        {
            map.Add(Enum.GetName(type, cur),cur);
        }
    }
    return map;
}
JaredPar
+3  A: 

Here's the LINQ-ified version of what you're trying to do. The key bit you're missing is checking the current value:

public static IDictionary<string, int> ToDictionary(this Enum enumeration)
{
    int value = (int)(object)enumeration;
    return Enum.GetValues(enumeration.GetType()).OfType<int>().Where(v => (v & value) == value).ToDictionary(v => Enum.GetName(enumeration.GetType(), v));
}

Edit: A little explanation on why we do the (int)(object)enumeration cast:

Bitwise operators only work against integral types (like int) and boolean, but Enum is not directly cast-able to int. So we must downcast to an object and presume that it actually represents an int as the underlying type - the compiler will allow us to cast an object to an int. If the enumeration is not integer-based, this will throw a runtime exception.

Rex M
very nice! Why did you cast enumeration to object first?
Keith
@unknown because bitwise operators only work against integral types (like int) and boolean, but Enum is not directly cast-able to int. So we must downcast to an object and *presume* that it actually represents an `int` as the underlying type - the compiler will allow us to cast an object to an int. If the enumeration is not integer-based, this will throw a runtime exception.
Rex M