Я думаю, чтобы получить полный ответ, люди должны знать, как перечисления работают внутри .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();
}