有關(guān)C#枚舉的問答集錦:基礎(chǔ)篇
Q:在C#里,我們?nèi)绾伪磉_(dá)枚舉類型?
A:你可以使用enum關(guān)鍵字(keyword)來聲明一個枚舉類型(enum type):
- // Code #01
- public enum Alignment
- {
- Left,
- Center,
- Right
- }
--------------------------------------------------------------------------------
Q:C#枚舉類型是值類型(value type)還是引用類型(reference type)?
A:枚舉類型都是值類型。
--------------------------------------------------------------------------------
Q:System.Enum是枚舉類型么?
A:不是。
--------------------------------------------------------------------------------
Q:System.Enum與枚舉類型(enum type)有什么關(guān)系?
A:System.Enum是一個抽象類(abstract class),所有枚舉類型都直接繼承自它,當(dāng)然也同時繼承了它的所有成員。
--------------------------------------------------------------------------------
Q:那么System.Enum屬于引用類型啦?
A:是的。
--------------------------------------------------------------------------------
Q:既然System.Enum是引用類型,而枚舉類型又是直接繼承自System.Enum的,那為什么枚舉類型卻不是引用類型?
A:這種繼承關(guān)系是隱式的并由編譯器負(fù)責(zé)展開,上面Code #1的Alignment枚舉被展開后的IL代碼如下:
- // Code #02
- .class public auto ansi sealed Aligment
- extends [mscorlib]System.Enum
- {
- .field public static literal Aligment Left = int32(0x00000000)
- .field public static literal Aligment Center = int32(0x00000001)
- .field public static literal Aligment Right = int32(0x00000002)
- .field public specialname rtspecialname int32 value__
- }
從聲明中,你可以看到Aligment的確是繼承自System.Enum的,只是你不能在C#里顯式聲明這種繼承關(guān)系。
--------------------------------------------------------------------------------
Q:但你好像沒有回答為什么枚舉類型繼承自一個引用類型后,卻還是值類型!
A:你知道,所有的值類型都是System.ValueType的后代,枚舉類型也不例外,枚舉類型直接繼承自System.Enum,而System.Enum卻又直接繼承自System.ValueType的,所以,枚舉類型也是System.ValueType的后代。
--------------------------------------------------------------------------------
Q:慢著!從System.ValueType派生出來的類型不都應(yīng)該是值類型嗎?為什么System.Enum會是引用類型?
A:正確的說法應(yīng)該是“值類型都是System.ValueType的后代”,但System.ValueType的后代不全是值類型,System.Enum就是***的特例!在System.ValueType的所有后代中,除了System.Enum之外其它都是值類型。事實(shí)上,我們可以在.NET的源代碼中找到System.Enum的聲明:
- public abstract class Enum : ValueType, IComparable, IFormattable, IConvertible
請注意,.NET Framework SDK v2.0.3600.0 Documentation中的Enum聲明是錯的:
public abstract struct Enum : IComparable, IFormattable, IConvertible
--------------------------------------------------------------------------------
Q:開始頭暈了,究竟C#枚舉類型、System.Enum、System.ValueType、值類型和引用類型之間存在著什么樣的關(guān)系?
A:簡單的說,
1. 所有枚舉類型(enum type)都是值類型。
2. System.Enum和System.ValueType本身是引用類型。
3. 枚舉類型(enum type)都是隱式的直接繼承自System.Enum,并且這種繼承關(guān)系只能由編譯器自動展開。但System.Enum本身不是枚舉類型(enum type)。
4. System.Enum是一個特例,它直接繼承自System.ValueType(參見Code #03),但本身卻是一個引用類型。
好吧,現(xiàn)在來看看下面代碼,你能猜得出它的輸出結(jié)果嗎?
- // Code #04
- static void Main()
- {
- Type t = typeof(System.Enum);
- if (t.IsEnum)
- Console.WriteLine("I'm enum type.");
- if (t.IsValueType)
- Console.WriteLine("I'm value type.");
- }
請別驚訝于程序的運(yùn)行結(jié)果沒有任何輸出!對于***個判斷,我們很清楚System.Enum并不是枚舉類型。但第二個判斷呢?System.Enum明明繼承自System.ValueType,卻不承認(rèn)是System.ValueType的后代!這是.NET上的一個特例,恰恰體現(xiàn)出System.Enum是特殊性。
--------------------------------------------------------------------------------
Q:既然枚舉類型是值類型,自然會涉及到裝箱和拆箱(boxing and unboxing)的問題,那么枚舉類型會被裝箱成什么呢?[Updated]
A:枚舉類型可以被裝箱成System.Enum、System.ValueType、System.Object或者System.IConvertible、System.IFormattable、System.IComparable。
注意:在.NET 1.1上,枚舉類型只能被裝箱到System.Enum、System.ValueType、System.Object;而在.NET 2.0上,枚舉類型還能被裝箱到System.Enum所實(shí)現(xiàn)的三個接口:System.IConvertible、System.IComparable、System.IFormattable。對應(yīng)的裝箱操作既可以為隱式的也可以是顯式的。
下面的C#代碼:
- // Code #05
- // See Code #01 for Alignment.
- static void Main()
- {
- Alignment a = Alignment.Center;
- Console.WriteLine(a.ToString());
- Console.WriteLine(a);
- }
對應(yīng)的IL代碼是:
- // Code #06
- .method private hidebysig static void Main() cil managed
- {
- .entrypoint
- // Code Size: 32 byte(s)
- .maxstack 1
- .locals (
- EnumerationFaq.Alignment alignment1)
- L_0000: ldc.i4.1
- L_0001: stloc.0
- L_0002: ldloc.0
- L_0003: box EnumerationFaq.Alignment
- L_0008: call instance string [mscorlib]System.Enum::ToString()
- L_000d: call void [mscorlib]System.Console::WriteLine(string)
- L_0012: nop
- L_0013: ldloc.0
- L_0014: box EnumerationFaq.Alignment
- L_0019: call void [mscorlib]System.Console::WriteLine(object)
- L_001e: nop
- L_001f: ret
- }
從IL代碼中我們可以看到枚舉類型被裝箱兩次。***次(L_0003)被裝箱成System.Enum,而第二次(L_0014)就被裝箱成System.Object。
但如果你讓編譯器自動為你選擇裝箱類型的話,它會優(yōu)先考慮System.Enum:
- // Code #07
- // See Code #01 for Alignment.
- class Program
- {
- static void Main()
- {
- Alignment a = Alignment.Center;
- Print(a);
- }
- static void Print(IConvertible c)
- {
- Console.WriteLine(c);
- }
- static void Print(IFormattable f)
- {
- Console.WriteLine(f);
- }
- static void Print(IComparable c)
- {
- Console.WriteLine(c);
- }
- static void Print(Object o)
- {
- Console.WriteLine(o);
- }
- static void Print(ValueType v)
- {
- Console.WriteLine(v);
- }
- static void Print(Enum e)
- {
- Console.WriteLine(e);
- }
- }
上面的代碼將被編譯成如下的IL:
- // Code #08
- .method private hidebysig static void Main(string[] args) cil managed
- {
- .entrypoint
- // Code Size: 15 byte(s)
- .maxstack 1
- .locals (
- EnumerationFaq.Alignment alignment1)
- L_0000: ldc.i4.1
- L_0001: stloc.0
- L_0002: ldloc.0
- L_0003: box EnumerationFaq.Alignment
- // 調(diào)用static void Print(Enum e);
- L_0008: call void EnumerationFaq.Program::Print([mscorlib]System.Enum)
- L_000d: nop
- L_000e: ret
- }
--------------------------------------------------------------------------------
以上就是有關(guān)C#枚舉類型的一些問答,更多問答待續(xù)。
【編輯推薦】