2014-01-17 35 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。 当它被铸造成季节 - 枚举时,我首先期望它失败。

我想,因为一个枚举引用类型,上的String.Format() - 变量“值”,则ToString()方法将被调用,这将返回它的名字。但是,当转换为int时,它将使用该“已命名”值的Property并返回int值。

如果有人能告诉我这是如何在窗帘后面工作的,我很高兴。我浏览了文件,但无法解决。

谢谢...


谢谢大家对你的答案!

但是,如果可以请多说一点关于C#内部的东西,我会非常感激。在Java中,枚举将是一个真正的引用类型,在内存中,它将被视为指向存储整数的另一个内存平衡的引用。非常非常简单。

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

在我看来,在C#中编译器,将它转移到像公共常量(在我的情况下是一个int)。请不要扔石头...

+0

System.Enum是一个引用类型,但是从它继承的类型是值类型。 – FSou1

回答

8

型四季的变量可以在基本类型(整数 在这个范围内分配任何价值案件)。值甚至不限于枚举的指定常量。所以这是完全合法的整数值5铸造Seasons类型实例四季值:

Seasons season = (Seasons)5; 

UPDATE:首先要注意 - System.Enum是值类型(以及它的自ValueType继承),如日期时间,的Int32 ,或布尔值。如果将眼光放在了四季枚举定义产生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); 

编译后会有枚举秋季会员的唯一价值:

.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数组)和名称(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类型。

+0

合法但不合逻辑。也许我们应该有hendecember,dodecember和tetrakaidecember作为月:-),当然,除非我们改变枚举,我们会称它们为13,14和15. – Jodrell

+0

@Jodrell我增加了更多的信息,告诉你它是怎么样的里面:) –

+1

太棒了,非常感谢。那正是我今天早上想要的。我想我必须深入到IL。现在我的所有问题都得到解答。 –

2

Enum.GetValues()的返回值是一个int数组,其值为0-3。当它变成一个季节 - 枚举我第一次预计它会失败..

不,它不是。返回类型为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) 
+0

更正这个例子,如果你在枚举中增加了另一个值'Fall = 30',你会得到一个可能意外的结果。我在我的回答中记录。 – Jodrell

+0

@Jodrell不错的警告,但这不是OP问题的关键。 –

1

Enum.GetValues(...)功能已被隐式转换回审问Enum的类型Enum返回值的System.Array

这可以证实这样

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

你会看到

四季

被写入到控制台。

由于EnumToString()返回成员名称的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表示enum中每个元素的基础类型是int。这是有道理的,因为它是一个枚举。因此,如果将枚举值转换为int,则将获得相应的int值。如果将整数转换为枚举类型,则将获得相应的枚举值。在你的情况下,例如(Season)2将返回Autumun.