Приведите int к перечислению в C # - PullRequest
2854 голосов
/ 27 августа 2008

Как можно преобразовать int в enum в C #?

Ответы [ 24 ]

3433 голосов
/ 27 августа 2008

из строки:

YourEnum foo = (YourEnum) Enum.Parse(typeof(YourEnum), yourString);
// the foo.ToString().Contains(",") check is necessary for enumerations marked with an [Flags] attribute
if (!Enum.IsDefined(typeof(YourEnum), foo) && !foo.ToString().Contains(","))
  throw new InvalidOperationException($"{yourString} is not an underlying value of the YourEnum enumeration.")

Из int:

YourEnum foo = (YourEnum)yourInt;

Обновление:

С номера вы также можете

YourEnum foo = (YourEnum)Enum.ToObject(typeof(YourEnum) , yourInt);
792 голосов
/ 27 августа 2008

Просто разыграйте:

MyEnum e = (MyEnum)3;

Вы можете проверить, находится ли он в диапазоне, используя Enum.IsDefined :

if (Enum.IsDefined(typeof(MyEnum), 3)) { ... }
220 голосов
/ 11 ноября 2011

В качестве альтернативы, используйте метод расширения вместо однострочного:

public static T ToEnum<T>(this string enumString)
{
    return (T) Enum.Parse(typeof (T), enumString);
}

Использование:

Color colorEnum = "Red".ToEnum<Color>();

OR

string color = "Red";
var colorEnum = color.ToEnum<Color>();
139 голосов
/ 03 апреля 2014

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

Как все работает

Перечисление в .NET - это структура, которая отображает набор значений (полей) в базовый тип (по умолчанию int). Тем не менее, вы можете выбрать тип целочисленного типа для перечисления:

public enum Foo : short

В этом случае enum сопоставляется с типом данных short, что означает, что он будет храниться в памяти как короткое и будет вести себя как короткое, когда вы используете его.

Если вы посмотрите на это с точки зрения IL, перечисление (normal, int) выглядит так:

.class public auto ansi serializable sealed BarFlag extends System.Enum
{
    .custom instance void System.FlagsAttribute::.ctor()
    .custom instance void ComVisibleAttribute::.ctor(bool) = { bool(true) }

    .field public static literal valuetype BarFlag AllFlags = int32(0x3fff)
    .field public static literal valuetype BarFlag Foo1 = int32(1)
    .field public static literal valuetype BarFlag Foo2 = int32(0x2000)

    // and so on for all flags or enum values

    .field public specialname rtspecialname int32 value__
}

Здесь следует обратить внимание, что value__ хранится отдельно от значений перечисления. В случае перечисления Foo выше тип value__ - это int16. В основном это означает, что вы можете хранить все, что вы хотите в перечислении, , пока типы соответствуют .

Здесь я хотел бы отметить, что System.Enum является типом значения, что в основном означает, что BarFlag займет 4 байта в памяти, а Foo займет 2 - например, размер базового типа (на самом деле это сложнее, но эй ...).

Ответ

Итак, если у вас есть целое число, которое вы хотите отобразить в перечисление, среда выполнения должна сделать только 2 вещи: скопировать 4 байта и назвать его как-нибудь еще (имя перечисления). Копирование неявно, потому что данные хранятся как тип значения - это в основном означает, что если вы используете неуправляемый код, вы можете просто обмениваться перечислениями и целыми числами без копирования данных.

Чтобы сделать это безопасным, я думаю, что лучше всего знать, что базовые типы являются одинаковыми или неявно конвертируемыми и гарантировать, что значения перечисления существуют (они не проверяются по умолчанию!)

Чтобы увидеть, как это работает, попробуйте следующий код:

public enum MyEnum : int
{
    Foo = 1,
    Bar = 2,
    Mek = 5
}

static void Main(string[] args)
{
    var e1 = (MyEnum)5;
    var e2 = (MyEnum)6;

    Console.WriteLine("{0} {1}", e1, e2);
    Console.ReadLine();
}

Обратите внимание, что приведение к e2 также работает! С точки зрения компилятора выше это имеет смысл: поле value__ просто заполняется либо 5, либо 6, а когда Console.WriteLine вызывает ToString(), имя e1 разрешается, а имя e2 - нет.

Если это не то, что вы хотели, используйте Enum.IsDefined(typeof(MyEnum), 6), чтобы проверить, соответствует ли значение, которое вы приводите, определенному перечислению.

Также обратите внимание, что я явно говорю о базовом типе перечисления, даже если компилятор действительно проверяет это. Я делаю это, чтобы избежать неожиданностей в будущем. Чтобы увидеть эти сюрпризы в действии, вы можете использовать следующий код (на самом деле я часто видел, как это происходит в коде базы данных):

public enum MyEnum : short
{
    Mek = 5
}

static void Main(string[] args)
{
    var e1 = (MyEnum)32769; // will not compile, out of bounds for a short

    object o = 5;
    var e2 = (MyEnum)o;     // will throw at runtime, because o is of type int

    Console.WriteLine("{0} {1}", e1, e2);
    Console.ReadLine();
}
109 голосов
/ 27 августа 2008

Возьмите следующий пример:

int one = 1;
MyEnum e = (MyEnum)one;
60 голосов
/ 21 октября 2011

Я использую этот фрагмент кода для приведения int к моему enum:

if (typeof(YourEnum).IsEnumDefined(valueToCast)) return (YourEnum)valueToCast;
else { //handle it here, if its not defined }

Я считаю это лучшим решением.

48 голосов
/ 07 сентября 2010

Ниже приведен хороший класс утилит для Enums

public static class EnumHelper
{
    public static int[] ToIntArray<T>(T[] value)
    {
        int[] result = new int[value.Length];
        for (int i = 0; i < value.Length; i++)
            result[i] = Convert.ToInt32(value[i]);
        return result;
    }

    public static T[] FromIntArray<T>(int[] value) 
    {
        T[] result = new T[value.Length];
        for (int i = 0; i < value.Length; i++)
            result[i] = (T)Enum.ToObject(typeof(T),value[i]);
        return result;
    }


    internal static T Parse<T>(string value, T defaultValue)
    {
        if (Enum.IsDefined(typeof(T), value))
            return (T) Enum.Parse(typeof (T), value);

        int num;
        if(int.TryParse(value,out num))
        {
            if (Enum.IsDefined(typeof(T), num))
                return (T)Enum.ToObject(typeof(T), num);
        }

        return defaultValue;
    }
}
42 голосов
/ 21 февраля 2013

Для числовых значений это безопаснее, так как будет возвращать объект независимо от того, что:

public static class EnumEx
{
    static public bool TryConvert<T>(int value, out T result)
    {
        result = default(T);
        bool success = Enum.IsDefined(typeof(T), value);
        if (success)
        {
            result = (T)Enum.ToObject(typeof(T), value);
        }
        return success;
    }
}
40 голосов
/ 01 ноября 2011

Если вы готовы к 4.0 .NET Framework, есть новая функция Enum.TryParse () , которая очень полезна и хорошо работает с атрибутом [Flags]. См. Метод Enum.TryParse (String, TEnum%)

30 голосов
/ 14 апреля 2011

Если у вас есть целое число, которое действует как битовая маска и может представлять одно или несколько значений в перечислении [Flags], вы можете использовать этот код для анализа значений отдельных флагов в списке:

for (var flagIterator = 0; flagIterator < 32; flagIterator++)
{
    // Determine the bit value (1,2,4,...,Int32.MinValue)
    int bitValue = 1 << flagIterator;

    // Check to see if the current flag exists in the bit mask
    if ((intValue & bitValue) != 0)
    {
        // If the current flag exists in the enumeration, then we can add that value to the list
        // if the enumeration has that flag defined
        if (Enum.IsDefined(typeof(MyEnum), bitValue))
            Console.WriteLine((MyEnum)bitValue);
    }
}

Обратите внимание, что это предполагает, что базовый тип enum является 32-разрядным целым числом со знаком. Если бы это был другой числовой тип, вам пришлось бы изменить жестко запрограммированный 32, чтобы отразить биты этого типа (или получить его программным путем, используя Enum.GetUnderlyingType())

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