tags:

views:

263

answers:

3

I'm writing a method which determines the highest value in a .NET enumeration so I can create a BitArray with one bit for each enum value:

pressedKeys = new BitArray(highestValueInEnum<Keys>());

I need this on two different enumerations, so I turned it into a generic method:

/// <summary>Returns the highest value encountered in an enumeration</summary>
/// <typeparam name="EnumType">
///   Enumeration of which the highest value will be returned
/// </typeparam>
/// <returns>The highest value in the enumeration</returns>
private static int highestValueInEnum<EnumType>() {
  int[] values = (int[])Enum.GetValues(typeof(EnumType));
  int highestValue = values[0];
  for(int index = 0; index < values.Length; ++index) {
    if(values[index] > highestValue) {
      highestValue = values[index];
    }
  }

  return highestValue;
}

As you can see, I'm casting the return value of Enum.GetValues() to int[], not to EnumType[]. This is because I can't cast EnumType (which is a generic type parameter) to int later.

The code works. But is it valid? Can I always cast the return from Enum.GetValues() to int[]?

+9  A: 

No, you can't safely cast to int[]. Enum types don't always use int as an underlying value. If you restrict yourself to enum types which do have an underlying type of int, it should be fine though.

This feels like something you (or I) could extend Unconstrained Melody to support if you wanted - in a way which genuinely constrained the type to be an enum type at compile time, and worked for any enum type, even those with underlying bases such as long and ulong.

Without Unconstrained Melody, you can still do this in a general way using the fact that all the enum types effectively implement IComparable appropriately. If you're using .NET 3.5 it's a one-liner:

private static TEnum GetHighestValue<TEnum>() {
  return Enum.GetValues(typeof(TEnum)).Cast<TEnum>().Max();
}
Jon Skeet
Jon, why does the Microsoft reference say "The default underlying type of the enumeration elements is int" if I can't assign any other type to an enum value, neither other integer types other than int32. I just tried to assign 3L to an enum value and got a compilation error.
Ciwee
Thanks for reporting the twisted assignment in the loop. I already discovered it myself and ninja'd a corrected version into place while you must have been writing your post ;-)
Cygon
@André Pena: The *default* underlying type is an enum, but you can declare an enum as "enum Foo : long" instead (etc). See the C# language specification for more details.
Jon Skeet
@Cygon: Will edit the post accordingly :)
Jon Skeet
@Jo Skeet: Now I understand it perfectly. I am able to assign a 3L value to an enum item as long I declare the enum as being a long enum. :) thank you.
Ciwee
A: 

Easy! Using LINQ your loop can be replaced with two lines of code, or just one if you want to munge it all together.

public partial class Form1 : Form
{
    private void Form1_Load(object sender, EventArgs e)
    {
        MyEnum z = MyEnum.Second;
        z++;
        z++;

        //see if a specific value is defined in the enum:
        bool isInTheEnum = !Enum.IsDefined(typeof(MyEnum), z);

        //get the max value from the enum:
        List<int> allValues = new List<int>(Enum.GetValues(typeof(MyEnum)).Cast<int>());
        int maxValue = allValues.Max();
    }


}

public enum MyEnum 
{
    Zero = 0,
    First = 1,
    Second = 2,
    Third = 3
}
slugster
Why bother creating a `List<int>` though? There's no need to buffer all the values...
Jon Skeet
You are totally right, i could have just written it as var max = (from int x in Enum.GetValues(typeof(MyEnum)).AsQueryable() select x).Max(); but that is not very friendly looking piece of code, i wanted to break it down a little and make the code easy to read and informative. And i don't think that "buffering the values" is any less optimal than looping over them to find the highest.
slugster
+1  A: 

As per Jon Skeet's advice (and thank you too, slugster), this is the updated code, now using IComparable because I'm still targeting .NET 2.0.

/// <summary>Returns the highest value encountered in an enumeration</summary>
/// <typeparam name="EnumType">
///   Enumeration of which the highest value will be returned
/// </typeparam>
/// <returns>The highest value in the enumeration</returns>
private static EnumType highestValueInEnum<EnumType>() where EnumType : IComparable {
  EnumType[] values = (EnumType[])Enum.GetValues(typeof(EnumType));
  EnumType highestValue = values[0];
  for(int index = 0; index < values.Length; ++index) {
    if(values[index].CompareTo(highestValue) > 0) {
      highestValue = values[index];
    }
  }

  return highestValue;
}

For anyone grabbing the code, you might want to add an additional check so it doesn't blow up on empty enums.

Cygon
Surely `Array.Sort<EnumType>(values);` would be better than your loop?
Stevo3000
It would be shorter, but why sort an array that I'm going to throw away after I'm done? Maybe I'm micro-optimizing here, but Array.Sort() just doesn't seem intuitive to me.
Cygon