可能出现Enum整型强制转换异常

本文关键字:转换 异常 整型 Enum | 更新日期: 2023-09-27 18:11:01

给定下面的代码,int强制转换是否有可能抛出异常?

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

可能出现Enum整型强制转换异常

是,如果enum支持类型不是int,如:

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

这将抛出。这是因为enum值与object一样是方框,并且您不能将'object'强制转换为'int',除非包含的值实际上是'int'。

你可以做一个Convert.ToInt32()来缓解这个问题,它将适用于所有int或更小的备份类型:

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

或者,如果您想假设int并且更安全,您可以检查enum的底层类型,如:

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

或者,您可以假设有符号的类型为long,无符号的类型为ulong(您可以有负的enum值,但往往较少):

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

这就是为什么做一些假设并在电话中检查它们可能更安全的原因。打开该值的任何操作都有可能抛出或溢出。

你甚至可以使用通用格式,让用户输入他们想要输出的类型:

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

你可以调用这个:

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

然后,如果他们得到一个异常,它落在他们指定正确的大小类型…

枚举是一种奇怪的东西。它们可以继承long,但仍然是枚举。

我很确定这段理论代码会抛出一个强制类型转换异常,如果你接受一个这样做的enum

正如James Michael Hare提到的,枚举可以是long。但是,使用Convert.ToInt32还不够好,因为您仍然可能获得溢出异常。想象一下下面的枚举,它的值甚至大于int类型所能容纳的值:

public enum BigEnum : long
{
    BigValue = (long)int.MaxValue + 5
}
在这种情况下,没有办法将该值转换为int,因为它太大了。但是,您可以使用这样的逻辑,它不会抛出:
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);
        }
    }
}

它使用Enum.GetUnderlyingType方法,以确保对ChangeType的调用将使用正确的类型。(事实上,GetUnderlyingType的MSDN页面有样例代码,几乎与我的样例代码所做的完全相同)。

Enum.GetValues实际上返回的是该特定enum类型的数组。

没有提到的是,如果TEnum:intTEnum:uint,特定的数组也可以转换为int[](无论签名如何,原语的CLR数组都是可转换的)。否则,为了安全起见,您可以始终使用Convert.ChangeType(object,Type) api。因此,您可以这样编写代码:

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