Cygon
Cygon

Reputation: 9620

Finding the Highest Value in an Enumeration

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[]?

Upvotes: 6

Views: 11095

Answers (4)

Mastax
Mastax

Reputation: 195

Using some modern C# features we can do this more elegantly:

static int MaxEnumValue<T>()
    where T : struct, Enum
{
    return (int)(ValueType)Enum.GetValues<T>().Max();
}

Upvotes: 1

Cygon
Cygon

Reputation: 9620

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.

Upvotes: 3

Jon Skeet
Jon Skeet

Reputation: 1501646

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();
}

Upvotes: 24

slugster
slugster

Reputation: 49985

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
}

Upvotes: 0

Related Questions