Возможно ли исключение Enum Integer Cast? - PullRequest
1 голос
/ 11 октября 2011

Учитывая приведенный ниже код, есть ли вероятность, что приведение int может вызвать исключение?

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

Ответы [ 4 ]

5 голосов
/ 11 октября 2011

Да, если тип поддержки 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));

Тогда, если они получают исключение, они должны указать правильный тип размера ...

2 голосов
/ 11 октября 2011

Перечисления странные звери.Они могут наследоваться от long и все еще быть перечислениями.

Я почти уверен, что этот теоретический код вызовет исключение приведения, если вы возьмете перечисление, которое делает это.

0 голосов
/ 11 октября 2011

Enum.GetValues фактически возвращает массив этого конкретного типа перечисления.

Не упоминается, что конкретный массив также можно преобразовать в int[] если TEnum:int или TEnum:uint (массивы 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));                      
              }
            }
        }
    }
0 голосов
/ 11 октября 2011

Как упоминал Джеймс Майкл Хэйр, перечисления могут быть 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 будет работать справильный тип.(на самом деле, эта страница MSDN для GetUnderlyingType содержит пример кода, который делает почти то же самое, что и мой пример кода).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...