1

Given the code below, is there any possiblity the int cast could throw an exception?

        static void foo(Type typeEnum)
        {
            if (typeEnum.IsEnum)
            {
                foreach (var enumVal in Enum.GetValues(typeEnum))
                {
                    var _val = (int)enumVal;                      
                }
            }
        }
6
  • GetValues returns an array. Implicitly typing could mean the compiler will choose object which can't be directly cast to int. Why not remove the implicit var and save yourself the headache? Commented Oct 11, 2011 at 15:29
  • @Jim: Removing the implicit typing won't save him. It will throw on the cast in the foreach then. You can cast from object to int as long as the object is truly boxing an int, just can't do a conversion cast. Commented Oct 11, 2011 at 15:31
  • @Jim in foreach loops explicitly typed loop variables are even more dangerous than var. The compiler will happily insert casts that would normally be explicit and can fail at runtime without telling you. In particular in this case using an int iteration variable will compile and fail at runtime in the same cases in which this code fails. [Once again the lack of generics in .net 1 caused warts in C#] Commented Oct 11, 2011 at 15:33
  • I have to ask the reason you are casting the emumeration, _val is not used at all, it serves no purpose, the only way it could cause an exception is if the enumVal cannot be casted to an integer. Commented Oct 11, 2011 at 15:34
  • @Ramhound - its just example code to illustrate my question clearly. Commented Oct 11, 2011 at 15:35

4 Answers 4

5

Yes, if the enum backing type is not int, like:

    public enum X : long
    {
        A,
        B,
        C
    }

This will throw. This is because the enum values are boxes as object, and you can't cast 'object' to 'int' unless the contained value is actually an 'int'.

You could alleviate this by doing a Convert.ToInt32() which will work for all backing types of int or smaller:

static void foo(Type typeEnum)
{
    if (typeEnum.IsEnum)
    {
        foreach (var enumVal in Enum.GetValues(typeEnum))
        {
            var _val = Convert.ToInt32(enumVal);                      
        }
    }
}

Or, if you want to assume int and just be safer, you can check the underlying type of the enum like:

if (Enum.GetUnderlyingType(typeEnum) != typeof(int))
{
    throw new ArgumentException("This method only accepts int enums.");
}

Alternatively, you could assume a type of long if signed or ulong if unsigned (you can have negative enum values, but tend to be rarer):

static void foo(Type typeEnum)
{
    if (typeEnum.IsEnum)
    {
        foreach (var enumVal in Enum.GetValues(typeEnum))
        {
            var _val = Convert.ToInt64(enumVal);                      
        }
    }
}

This is why it's probably safer to make some assumptions and check them on the call. Anything you do to unbox the value has the potential of throwing or overflowing.

You could even go generic and have the user pass in the type they want to get out:

static IEnumerable<ToType> foo<ToType>(Type typeEnum)
{
    if (typeEnum.IsEnum)
    {
        foreach (var enumVal in Enum.GetValues(typeEnum))
        {
            yield return (ToType)Convert.ChangeType(enumVal, typeof(ToType));
        }
    }
}

So you could invoke this:

IEnumerable<int> values foo<int>(typeof(YourEnum));

Then if they get an exception, it falls on them to specify the right size type...

Sign up to request clarification or add additional context in comments.

3 Comments

Convert.ToInt32 can still throw an OverflowException if the enum's value is too big to fit in an int.
@Stephen: Of course, but it would get around the casting issue. At that point it's up to the OP to decide what to do. My suggestion is to either check for non int enum types, or the OP can always return a long.
+1 For the type conversion method. I've used a very similar extension method to do the same.
2

Enums are strange beasts. They may inherit from long and still be enums.

I'm pretty sure this theoretical code would throw a cast exception should you take an enum that does this.

Comments

0

As James Michael Hare mentioned, enums can be longs. However, using Convert.ToInt32 is not good enough because you could still get an overflow exception. Imagine the following enum that has a value that is bigger than can even fit in an int:

public enum BigEnum : long
{
    BigValue = (long)int.MaxValue + 5
}

In that case, there is no way to convert that value to an int because it is too big. However, you can use logic like this and it will not throw:

static void foo(Type typeEnum)
{
    var underlyingType = Enum.GetUnderlyingType(typeEnum);

    if (typeEnum.IsEnum)
    {
        foreach (var enumVal in Enum.GetValues(typeEnum))
        {
            var _val = Convert.ChangeType(enumVal, underlyingType);
        }
    }
}

It uses the Enum.GetUnderlyingType method which ensures that the call to ChangeType will be working with the correct type. (in fact, that MSDN page for GetUnderlyingType has sample code that does almost this exact thing my sample code does).

2 Comments

Unfortunately, that just removes the issue back one level. He'd have a value boxed in an object (ChangeType returns object) that he can't cast back to int or long directly. Essentially, all this does is convert the boxed long back into a boxed long and leaves him in the same situation.
@JamesMichaelHare I was just answering the OP's question perhaps too literally. He asked if the int cast could throw. It can. And so can a call to Convert.ToInt32. But yes, it all depends on what _val is to be used for.
0

Enum.GetValues actually returns an array of that particular enum type.

What's not mentioned is that the particular array is also castable to int[] if TEnum:int or TEnum:uint (CLR arrays for primitives are convertible regardless of signedness) Otherwise to be defensive you can always use the Convert.ChangeType(object,Type) api to be safe. As a result you can write your code to be like so:

    static void foo(Type typeEnum)
    {
        if (typeEnum.IsEnum)
        {
            var array = Enum.GetValues(typeEnum)
            int[] arrayAsInts = array as int[];
            if(arrayAsInts != null)
            { 
              foreach (var enumVal in arrayAsInts)
              {
                 var _val = enumVal;                      
              }
            }
            else
            {
              foreach (var enumVal in array)
              {
                 var _val = Convert.ChangeType(enumVal,typeof(int));                      
              }
            }
        }
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.