2014-01-17 54 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.