2014-01-17 3 views
3

При чтении исходного кода я наткнулся на фрагмент C#, поведение которого запутывает меня. Прежде чем я начну спрашивать, вот адаптированный пример:Внутренняя работа Enum.GetValues ​​()

Перечисление:

private enum Seasons 
{ 
    Spring, 
    Summer, 
    Autumn, 
    Winter 
} 

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

foreach(Seasons value in Enum.GetValues(typeof(Seasons))) 
{ 
    Console.WriteLine(String.Format("{0} {1}", value, (int) value)); 
} 

Результат выглядит следующим образом:

Spring 0 
Summer 1 
Autumn 2 
Winter 3 

Мой вопрос:

Как это работает внутри C#? Возвращаемая стоимость от:

Enum.GetValues(typeof(Seasons)) 

- массив int, заполненный значениями 0-3. Когда он попадает в список Seasons-enum, я сначала ожидаю, что он потерпит неудачу.

Я думаю, так как перечисление является ссылочного типа на String.Format() - аргумент «значение», метод ToString() будет называться, который будет возвращать его имя. Но когда он передается в int, он будет использовать Свойство в этом значении «named» и вернет значение int.

Я буду счастлив, если кто-нибудь скажет мне, как это работает за шторами. Я просмотрел документацию, но пока не могу ее обработать.

Спасибо ...


Спасибо всем за ответы!

Но ... Я бы очень признателен, если бы можно было сказать немного больше о внутренних функциях C#. В Java перечисление будет реальным ссылочным типом, а в памяти оно будет рассматриваться как ссылка, указывающая на другой мир памяти, в котором хранятся целые числа. Очень, очень упрощенно.

---------    ----------------- 
|  | Reference |int  Spring | 
|Seasons|-------------> |int  Summer | 
|  |    |int  Autumn | 
---------    |int  Winter | 
         ----------------- 

Мне кажется, что в C# компилятор, передает его что-то вроде общественного сопзЬ (в моем случае, если Int). Пожалуйста, не бросайте камни ...

+0

System.Enum - ссылочный тип, но типы, наследующие от него, являются типами значений. – FSou1

ответ

8

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

Seasons season = (Seasons)5; 

UPDATE: Первое, что нужно отметить - System.Enum является типом значения (и это унаследовал от ValueType), как DateTime, Int32 , или Boolean. Если вы будете смотреть на IL, сгенерированный для определения сезонов перечислений, вы увидите

.class public auto ansi sealed Seasons 
    extends [mscorlib]System.Enum 
{ 
    .field public static literal valuetype Foo.Seasons Autumn = int32(2) 
    .field public static literal valuetype Foo.Seasons Spring = int32(0) 
    .field public static literal valuetype Foo.Seasons Summer = int32(1) 
    .field public specialname rtspecialname int32 value__ 
    .field public static literal valuetype Foo.Seasons Winter = int32(3) 
} 

Итак, это совокупность общественных статических полей, которые помечены как литералы - что делает компилятор встраивание значения перечисления непосредственно в код. Например. Следующий код

Seasons season = Seasons.Autumn; 
Console.WriteLine(season); 

После компиляции будет только значение элемента Autumn перечислений:

.locals init (
    [0] valuetype Foo.Seasons seasons) // it's value type! 
L_0000: nop 
L_0001: ldc.i4.2 // here simple integer value 2 is loaded 
L_0002: stloc.0 
L_0003: ldloc.0 
L_0004: box Foo.Seasons 
L_0009: call void [mscorlib]System.Console::WriteLine(object) 

Таким образом, на самом деле у вас есть значение базового типа присвоенного переменной перечислимого типа. Никаких специальных свойств для имени или ценности. Просто (любая) целая константа.

Теперь о получении всех значений перечислимого - при вызове Enum.GetValues возвращает значения из частных HashtablefieldInfoHash которые вы можете найти в Enum классе. То, что Hashtable кэширует имена и значения перечислений. Итак, когда вы впервые получаете значения вашего перечисления, HashEntry со значениями (ulong array) и именами (string array) для типа перечисления. На самом деле этот кеш будет использоваться, если вы вызовете другие методы Enum, например GetNames или IdDefined.

После ввода кэшированной записи для вашего типа перечисления возвращается массив значений. Но с одним трюком. Это не простой массив значений ulong. Каждое значение упаковывается как тип перечисления с Enum.ToObject вызова (вы можете найти в реализации System.RuntimeType):

ulong[] values = Enum.InternalGetValues(this); 
Array array = Array.UnsafeCreateInstance(this, values.Length); 
for (int i = 0; i < values.Length; i++) 
{ 
    object obj2 = Enum.ToObject(this, values[i]); 
    array.SetValue(obj2, i); 
} 
return array; 

Именно поэтому каждый объект значение будет иметь тип Seasons вместо ulong типа.

+0

правовой, но не логичный. Может быть, мы должны иметь hendecember, dodecember и tetrakaidecember как месяцы тоже :-), конечно, если мы не изменим перечисление, мы будем называть их 13, 14 и 15. – Jodrell

+0

@Jodrell Я добавил больше информации, чтобы показать вам, как это выглядит внутри :) –

+1

Удивительный, большое спасибо. Это было именно то, что я искал сегодня утром. Думаю, мне нужно глубже проникнуть в ИЛ. Теперь все мои вопросы отвечают. –

2

Возвращаемое значение из Enum.GetValues ​​() - массив int, заполненный значениями 0-3. Когда он попадает в список Seasons-enum, я сначала ожидаю, что он потерпит неудачу.

Нет, это не так. Тип возврата - Array и который содержит значения констант в enumType. Поэтому вполне законно его бросать.

Что касается ИНТ значений, которые вы обнаружили там, это от MSDN

По умолчанию базовый тип каждого элемента в перечислении является внутр. Вы можете указать другой целочисленный числовой тип. Утвержденными типами перечисления являются байт, sbyte, short, ushort, int, uint, long или ulong.

Если вы не указали нумерацию, она будет назначать числа, начинающиеся с 0, как вы обнаружили. Тем не менее, вы можете использовать любые номера вы хотите

private enum Seasons 
{ 
    Spring = 10, 
    Summer = 20, 
    Autumn = 30, 
    Winter = 40 
} 

и сравнить его с номерами, а

if ((int) Seasons.Spring == 10) 
+0

Исправьте этот пример, если вы добавите другое значение в перечисление, 'Fall = 30,' вы получите результат, который может быть неожиданным. Я записал в своем ответе. – Jodrell

+0

@Jodrell Ни о чем не говори, но это не вопрос вопроса OP. –

1

Enum.GetValues(...) функция возвращает System.Array значений в Enum, которые были неявно отбрасываемой назад к типу обследуемой Enum.

Это может быть подтверждено как этот

var valEnum = Enum.GetValues(typeof(Seasons)).GetEnumerator(); 
valEnum.MoveNext(); 
Console.WriteLine(valEnum.Current.GetType()); 

Вы увидите, что

Seasons

записывается в консоль.

Поскольку ToString() в виде Enum возвращает string имени члена и, когда int полученный Enum член приводится к int он возвращает значение int, ваш код работает, как это делает.


После прочтения ответа выше, вы можете быть удивлены, как я знаю, и зачем мне, если Enum значения были неявно назад в System.Array, хорошо рассмотреть этот Enum

enum Seasons 
{ 
    Spring = 0, 
    Summer = 1, 
    Autumn = 2, 
    Fall = 2, 
    Winter = 3 
} 

Если я опрашивать этот Enum, как это,

foreach(var e in Enum.GetValues(typeof(Seasons)) 
{ 
    Console.WriteLine(
     "{0}: {1}: {2}", 
     e.GetType().Name, 
     e, 
     (int) e); 
} 

получить результаты

Времена года: Весна, 0

Времена года: Лето, 1

Сезоны: Осень, 2

Сезоны: Осень, 2

Времена года: Зима, 3

это, вероятно, не то, что вы ожидали. Второе значение для 2, Fall в определении было неявным образом возвращено к первому элементу Seasons с соответствующим значением. В этом примере Autumn.


Кстати, тест

var valEnum = Enum.GetValues(typeof(Seasons)).GetEnumerator(); 
valEnum.MoveNext(); 
Console.WriteLine(valEnum.Current.GetType().IsValueType); 

выход будет,

Правда

показывает, что Enums типы значений, но различие является спорным по отношению к вашему вопросу ,

1

Поскольку documentation говорит, что базовый тип каждого элемента в перечислении является int. И это имеет смысл, потому что это перечисление . Поэтому, если вы набросаете значение enum на int, вы получите соответствующее значение int. Если вы передадите целое число в тип перечисления, вы получите соответствующее значение перечисления.В вашем случае, например, (Season)2 вернет Autumun.

Смежные вопросы