Enum.GetValues()的内部工作

Dan*_*tes 3 c# enums

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

枚举:

private enum Seasons
{
    Spring,
    Summer,
    Autumn,
    Winter
}
Run Code Online (Sandbox Code Playgroud)

用法:

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

输出如下所示:

Spring 0
Summer 1
Autumn 2
Winter 3
Run Code Online (Sandbox Code Playgroud)

我的问题:

这在C#中如何工作?来自的返回值:

Enum.GetValues(typeof(Seasons))
Run Code Online (Sandbox Code Playgroud)

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

我认为由于枚举是一个引用类型,在String.Format() - 参数"value"上,将调用ToString()方法,该方法将返回其名称.但是当转换为int时,它将在"named"值上使用Property并返回int值.

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

谢谢...


谢谢大家的答案!

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

---------               -----------------
|       |  Reference    |int     Spring |
|Seasons|-------------> |int     Summer |
|       |               |int     Autumn |
---------               |int     Winter |
                        -----------------
Run Code Online (Sandbox Code Playgroud)

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

Ser*_*kiy 9

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

Seasons season = (Seasons)5;
Run Code Online (Sandbox Code Playgroud)

更新:首先要注意的事项 - 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)
}
Run Code Online (Sandbox Code Playgroud)

因此,这是一组公共静态字段,标记为文字 - 这使得编译器将枚举值直接嵌入到代码中.例如下面的代码

Seasons season = Seasons.Autumn;
Console.WriteLine(season);
Run Code Online (Sandbox Code Playgroud)

编译后,只有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)
Run Code Online (Sandbox Code Playgroud)

因此,实际上您具有分配给枚举类型变量的基础类型的值.没有名称或值的特殊属性.只是(任意)整数常量.

现在关于获取枚举的所有值 - 当你调用Enum.GetValues它时,返回私有的值Hashtable fieldInfoHash,你可以在Enum课堂上找到它们.这会Hashtable缓存枚举的名称和值.因此,当您第一次获得枚举值时,将创建枚举类型的HashEntry值(ulong数组)和名称(string数组).实际上,如果您调用其他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;
Run Code Online (Sandbox Code Playgroud)

这就是为什么每个值对象都是Seasons类型而不是ulong类型.