Enum.GetValues()的内部工作

时间:2014-01-17 10:09:19

标签: c# enums

在阅读某人的源代码时,我遇到了一个C#片段,其行为让我感到困惑。在我开始询问之前,这是一个改编的例子:

枚举:

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

是一个填充值0-3的int数组。 当它被送到季节时,我首先期待它失败。

我认为因为 enum 引用类型,所以在String.Format() - 参数“value”上,将调用ToString()方法,将返回其名称。但是当转换为int时,它将在“named”值上使用Property并返回int值。

如果有人能告诉我窗帘背后是如何工作的,我会很高兴。我查看了文档,但还是无法解决。

谢谢...


谢谢大家的答案!

但是......我真的很感激,如果有人可以再多谈一下C#的内部结构。在Java中,枚举将是一个真正的引用类型,并且在内存中,它将被视为指向存储整数的另一个内存平衡的引用。非常非常简化。

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

在我看来,在C#编译器中,将它转换为类似公共const的东西(在我的例子中是一个int)。请不要扔石头......

4 个答案:

答案 0 :(得分:9)

季节类型的变量可以在基础类型的范围内分配任何值(整数 在这种情况下)。值甚至不限于枚举的命名常量。因此,通过将整数值5转换为Seasons类型来实例化季节值是完全合法的:

Seasons season = (Seasons)5;

更新:首先要注意的事项 - 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);

编译后,只有enum秋季成员的价值:

.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时,它会返回私人Hashtable fieldInfoHash中的值,您可以在Enum课程中找到这些值。 Hashtable缓存枚举的名称和值。因此,当您第一次获得枚举值时,将创建具有值(HashEntry数组)的ulong和用于枚举类型的名称(string数组)。实际上,如果您要调用其他Enum方法,例如GetNamesIdDefined,则会使用此缓存。

在为枚举类型缓存条目后,将返回值数组。但有一个技巧。这不是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;

这就是为什么每个值对象都是季节类型而不是ulong类型。

答案 1 :(得分:2)

  

Enum.GetValues()的返回值是一个填充值0-3的int数组。当它被送到季节 - 枚举时,我首先期望它失败..

不,不是。返回类型为Array,其中包含 enumType 中常量的值。因此施展它是完全合法的。

至于你在那里发现的int值,这是来自MSDN

  

默认情况下,枚举中每个元素的基础类型为int。您可以指定另一个整数数字类型。枚举的已批准类型是byte,sbyte,short,ushort,int,uint,long或ulong。

如果您未指定任何编号,则会在您发现时从0开始分配编号。但是,您可以使用您想要的任何数字

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

并将其与数字进行比较

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

答案 2 :(得分:1)

Enum.GetValues(...)函数返回System.ArrayEnum个值,这些值已隐式转换回询问Enum的类型。

这可以像这样确认

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

你会看到

  

四季

写入控制台。

由于ToString()的{​​{1}}会返回成员名称的Enum,并且当string派生的int成员投放到Enum时1}}它返回int值,您的代码就像它一样工作。


在阅读了上面的答案之后,您可能想知道,我如何知道以及为什么我关心int值是否已隐含地返回Enum,请考虑这个{{1 }}

System.Array

如果我像这样询问Enum

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

我得到了结果

  

季节:春天,0

     

季节:夏天,1

     

季节:秋天,2

     

季节:秋天,2

     

季节:冬天,3

这可能不是你所期待的。定义中Enumforeach(var e in Enum.GetValues(typeof(Seasons)) { Console.WriteLine( "{0}: {1}: {2}", e.GetType().Name, e, (int) e); } 的第二个值已隐式转换回具有匹配值的第一个2成员。在此示例中Fall


顺便提一下,测试

Seasons

将输出,

  

显示Autumn是值类型,但区别与您的问题无关。

答案 3 :(得分:1)

正如documentation所说枚举中每个元素的基础类型是int。这是有道理的,因为它是枚举。因此,如果你强制转换对于int的枚举值,您将获得相应的int值。如果将整数转换为枚举类型,您将获得相应的枚举值。例如,(Season)2将返回Autumun.