开发者

Possible for Enum Integer Cast Exception?

开发者 https://www.devze.com 2023-04-12 01:34 出处:网络
Given the code below, is there any possiblity the int cast could throw an exception? static void foo(Type typeEnum)

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;                      
                开发者_运维百科}
            }
        }


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...


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.


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).


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

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号