有關(guān)C#枚舉的問答集錦:有關(guān)賦值
本文繼續(xù)介紹C#枚舉的常見問題與答案。
Q:我留意到Code #02中的
.field public static literal Aligment Center = int32(0x00000001)
該語句明顯是整數(shù)賦值,這是否說明C#枚舉類型實質(zhì)上是整數(shù)類型?
A:這說明枚舉類型與整數(shù)類型的確有一定的關(guān)系。事實上,每一個枚舉類型都有與之相對應的整數(shù)類型,我們稱該整數(shù)類型為底層類型(underlying type),默認的情況下使用,.NET使用System.Int32。當然,你可以手動將其指定為其他的整數(shù)類型:
- // Code #09
- public enum Alignment : byte
- {
- Left,
- Center,
- Right
- }
注意,能被指定為枚舉的底層類型的只能是如下所列的整數(shù)類型:byte, sbyte, short, ushort, int, uint, long, ulong。
--------------------------------------------------------------------------------
Q:為何我們需要指定枚舉類型的底層類型?
A:你完全可以讓它接受默認的底層類型。請留意Code #08,你完全找不到“Center”這個字眼,然而在C#代碼中,它卻是存在的,為什么呢?這是因為代碼在編譯的時候,編譯器把枚舉類型轉(zhuǎn)換為與之對應的底層類型的數(shù)值來處理。Code #08的L_0000實際上就是把類型為System.Int32的數(shù)值1推入堆棧,而不是把“Center”推入堆棧。事實上,底層類型說明了如何為枚舉類型分配空間,不同的底層類型所占用的資源不同,大概當你在受限系統(tǒng)上進行開發(fā)的話,你就可能需要注意一下了。
--------------------------------------------------------------------------------
C#枚舉的賦值
Q:枚舉成員的值是怎樣規(guī)定的?
A:如果你沒有手動指定成員的值的話,從上往下看,各成員的值為:0, 1, 2, ...。說罷了,就是一個非負整數(shù)等差數(shù)列,其初值為0,步長為1。例如:
- // Code #10
- public enum Alignment
- {
- Left, // 0
- Center, // 1
- Right // 2
- }
--------------------------------------------------------------------------------
Q:如果我有手動指定某些成員的值呢?
A:那么被賦值的成員的值就是你所指定的值。當然,無論你是否手動指定枚舉成員的值,遞增步長都不會變,總是為1。為了測試你是否理解,請說出下面枚舉個成員的值以及你的判斷理由(請用人腦而不是電腦來運行以下代碼):
- // Code #11
- public enum DriveType : sbyte
- {
- CDRom,
- Fixed = -2,
- Network,
- NoRootDirectory = -1,
- Ram,
- Removable = Network * NoRootDirectory,
- Unknown
- }
--------------------------------------------------------------------------------
Q:我們?nèi)绾潍@取枚舉成員的值,無論成員是否被手動賦值?
A:你可以使用System.Enum的
- public static Array GetValues(Type enumType);
該方法返回一個包含所有枚舉成員的數(shù)組:
- // Code #12
- // See Code #01 for Alignment.
- public static void Main()
- {
- Alignment[] alignments = (Alignment[])Enum.GetValues(typeof(Alignment));
- Console.WriteLine("Wanna see the values of Alignment's menbers?");
- foreach (Alignment a in alignments)
- Console.WriteLine("{0:G} = {0:D}", a);
- }
- // Output:
- // Wanna see the values of Alignment's menbers?
- // Left = 0
- // Center = 1
- // Right = 2
--------------------------------------------------------------------------------
Q:如果我只需要其中某些枚舉成員的值呢?
A:那么你可以把枚舉轉(zhuǎn)換為IConvertible接口,再調(diào)用對應的方法:
- // Code #12
- // See Code #01 for Alignment.
- public static void Main()
- {
- IConvertible ic = (IConvertible)Alignment.Center;
- int i = ic.ToInt32(null);
- Console.WriteLine("The value of Alignment.Center is {0}.", i);
- }
- // Output:
- // The value of Alignment.Center is 1.
--------------------------------------------------------------------------------
Q:為什么需要手動指定枚舉成員的值?
A:一般情況下,使用默認的賦值規(guī)則就足夠了,但某些情況下,為枚舉成員指定一個與實際情況(模型)相符的值可能更有意義,這要視你具體所建的模型而定。
還是讓我們來一個實際的例子:
- // Code #13
- public enum CustomerKind
- {
- Normal = 90,
- Vip = 80,
- SuperVip = 70,
- InActive = 100
- }
- public class Customer
- {
- public readonly CustomerKind Kind;
- private double m_Payment;
- public double Payment
- {
- return m_Payment * (int)Kind / 100;
- }
- // Code here
- }
我為枚舉CustomerKind的每個成員都賦了一個特定的值,該值其實就是顧客購物折扣百分率。而在Customer類中,Payment屬性就通過強類型轉(zhuǎn)換來獲取枚舉成員的值(也就是購物折扣率),并用于貨款計算。從這里可以看出,獲取枚舉成員的值還可以通過強類型轉(zhuǎn)換方式。
--------------------------------------------------------------------------------
Q:既然枚舉類型可以強制轉(zhuǎn)換為整數(shù),那么整數(shù)是否也可以強制轉(zhuǎn)換為枚舉類型?
A:答案是肯定的。
- // Code #14
- // See Code #01 for Alignment.
- Alignment a = (Alignment)1;
但這種機制可能使你遇到一些麻煩:
- // Code #15
- // See Code #01 for Alignment.
- class Program
- {
- static void Main()
- {
- Foo((Alignment)12345);
- }
- static void Foo(Alignment a)
- {
- // Code here
- }
- }
你無法避免有人進行這樣的惡作?。?!
--------------------------------------------------------------------------------
Q:那么是否有辦法對付這些惡作劇的人?
A:Sure!我們總不能假設(shè)人人都那么守規(guī)矩,所以,我們需要System.Enum的
- public static bool IsDefined(Type enumType, object value);
現(xiàn)在我們把Code #15的Foo方法改進一下:
- // Code #16
- // See Code #01 for Alignment.
- static void Foo(Alignment a)
- {
- if (!Enum.IsDefined(typeof(Alignment), a))
- throw new ArgumentException("DO NOT MAKE MISCHIEF!");
- // Code here
- }
這樣,惡作劇的人將會收到一個警告(異常消息)。當然,我們不排除有人是由于一時大意才造成這樣的“惡作劇”,那么IsDefined方法同樣可以幫助你處理好這些情況。
--------------------------------------------------------------------------------
Q:我認為我們還可以使用條件判斷語句來處理這種情況:
- // Code #17
- // See Code #01 for Alignment.
- static void Foo(Alignment a)
- {
- if (a != Alignment.Left &&
- a != Alignment.Center &&
- a != Alignment.Right)
- throw new ArgumentException("DO NOT MAKE MISCHIEF!");
- // Code here
- }
或者
- // Code #18
- // See Code #01 for Alignment.
- static void Foo(Alignment a)
- {
- switch(a)
- {
- case Alignment.Left:
- Console.WriteLine("Cool~");
- break;
- case Alignment.Center:
- Console.WriteLine("Well~");
- break;
- case Alignment.Right:
- Console.WriteLine("Good~");
- break;
- default:
- Console.WriteLine("DO NOT MAKE MISCHIEF!");
- break;
- }
- }
A:你絕對可以這樣做!事實上,如果你處于以下情況之一的話:
1. Alignment枚舉代碼不會被修改
2. 你不希望使用Alignment枚舉新增的特性
那么我會推薦使用你的處理方式。而且,你還可以為自己的代碼定義一個這樣的方法:
- // Code #19
- // See Code #01 for Alignment.
- public static bool IsAlignment(Alignment a)
- {
- switch(a)
- {
- case Alignment.Left:
- return true;
- case Alignment.Center:
- return true;
- case Alignment.Right:
- return true;
- default:
- return false;
- }
- }
這個方法比起IsDefine方法高效多了。
--------------------------------------------------------------------------------
以上就總結(jié)了一些C#枚舉賦值的相關(guān)問答。
【編輯推薦】