有關(guān)C#枚舉的問答集錦:使用情景
之前介紹了C#枚舉的基礎(chǔ)與賦值相關(guān)的知識,本文繼續(xù)介紹有關(guān)C#枚舉的一些問答。
Q:我定義了一個這樣的枚舉:
- // Code #20
- public enum FontStyle
- {
- Bold,
- Italic,
- Regular,
- Strikethrough,
- Underline
- }
我用它來指定字體的風(fēng)格,但我遇到了麻煩。你知道,字體可以同時擁有枚舉里面所列舉的一種或者多種風(fēng)格,那么,我如何為字體同時指定多種風(fēng)格呢?
A:這個時候你就需要位枚舉(Bit Flags),把Code #20修改一下:
- // Code #21
- // I am using the FlagsAttribute to identify a bit flags.
- [Flags]
- public enum FontStyle
- {
- Bold = 0x0001,
- Italic = 0x0002,
- Regular = 0x0004,
- Strikethrough = 0x0010,
- Underline = 0x0020
- }
現(xiàn)在,你可以通過按位或運算來為字體指定多種風(fēng)格了:
- // Code #22
- // See Code #21 for FontStyle.
- Font f = new Font(
- FontFamily.GenericSansSerif,
- 12.0F,
- FontStyle.Italic | FontStyle.Underline
- );
--------------------------------------------------------------------------------
Q:位枚舉同樣存在類似于Code #15的惡作劇吧?
A:是的,例如:
- // Code #23
- // See Code #21 for FontStyle.
- class Program
- {
- static void Main()
- {
- Bar(FontStyle.Regular | (FontStyle)0x0400);
- }
- static void Bar(FontStyle fs)
- {
- // Code here
- }
- }
--------------------------------------------------------------------------------
Q:那么,System.Enum.IsDefine方法是否還能應(yīng)對呢?
A:不能。位枚舉成員并不具備排他性,多個成員可以通過按位或運算組合起來。而System.Enum.IsDefine方法只能判斷枚舉變量的值是否為某一已定義的枚舉成員。請看如下代碼:
- // Code #24
- // See Code #21 for FontStyle.
- FontStyle fs1 = FontStyle.Bold | FontStyle.Italic | FontStyle.Underline;
- Console.WriteLine(Enum.IsDefine(typeof(FontStyle), fs1));
- FontStyle fs2 = FontStyle.Regular | (FontStyle)0x0400;
- Console.WriteLine(Enum.IsDefine(typeof(FontStyle), fs2));
- // Output:
- // false
- // false
我們對代碼的輸出毫無疑問,因為fs1和fs2都不是一個單獨的枚舉成員。但這不是我們所追求的答案,我們希望區(qū)別對待fs1和fs2,至少我們不希望fs2中的搗蛋家伙——(FontStyle)0x0400——在我們的程序中搞破壞!
--------------------------------------------------------------------------------
Q:那么,我們是否有辦法隔離這些搗蛋鬼呢?
A:Of course!我們同樣可以使用條件判斷語句來處理,但做法將與Code #17和Code #18有所不同。現(xiàn)在我們把Code #23改進如下:
- // Code #25
- // See Code #21 for FontStyle.
- class Program
- {
- static void Main()
- {
- Bar(FontStyle.Regular | (FontStyle)0x0400);
- }
- static void Bar(FontStyle fs)
- {
- if ((fs & FontStyle.Bold) != 0)
- {
- // Do something associated with bold
- }
- if ((fs & FontStyle.Italic) != 0)
- {
- // Do something associated with italic
- }
- // Other conditional code continues here
- }
- }
我們把枚舉變量與某一特定的位枚舉成員進行按位與運算,若結(jié)果不為0則表明枚舉變量中包含著該位枚舉成員。當(dāng)然,你也可以為自己的代碼寫一組這樣的方法:
- // Code #26
- // See Code #21 for FontStyle.
- static bool ContainsBold(FontStyle fs)
- {
- if ((fs & FontStyle.Bold) != 0)
- return true;
- else
- return false;
- }
- static bool ContainsItalic(FontStyle fs)
- {
- if ((fs & FontStyle.Italic) != 0)
- return true;
- else
- return false;
- }
- // Other similar methods continue here
又或者你可以寫一個這樣的方法:
- // Code #27
- // See Code #21 for FontStyle.
- static bool ContainsMember(FontStyle fs, FontStyle menber)
- {
- if ((fs & member) != 0)
- return true;
- else
- return false;
- }
如果你只希望判斷某一個枚舉變量里面是否包含搗蛋鬼,你可以寫一個這樣的方法:
- // Code #28
- // See Code #21 for FontStyle.
- static bool ContainsPranksters(FontStyle fs)
- {
- if ((fs < FontStyle.Bold) || (fs > (FontStyle)0x0037))
- return true;
- if (fs == (FontStyle)0x0008 ||
- fs == (FontStyle)0x0009 ||
- fs == (FontStyle)0x0018 ||
- fs == (FontStyle)0x0019 ||
- fs == (FontStyle)0x0028 ||
- fs == (FontStyle)0x0029)
- return true;
- return false;
- }
留個“作業(yè)”吧,知道為何這樣可以判斷出是否有搗蛋鬼嗎?當(dāng)然,如果你想到了更好的方法,記住要告訴我喲!
--------------------------------------------------------------------------------
Q:慢著!你那個“我們把枚舉變量與某一特定的位枚舉成員進行按位與運算,若結(jié)果不為0則表明枚舉變量中包含著該位枚舉成員”,在以下的情況顯然不成立的:
- // Code #35
- [Flags]
- enum Music
- {
- Jazz = 0x00,
- Rock = 0x01,
- Country = 0x02,
- Classic = 0x03
- }
- static void Main()
- {
- Music m = Music.Rock | Music.Jazz;
- int r = (int)(m & Music.Classic);
- Console.WriteLine(r);
- }
該代碼的輸出恰恰就為0,然而m卻不包含Music.Classic!
A:Good question!也正如你所看到的,這種做法其實與位枚舉成員的值是如何被賦予息息相關(guān)的。那么,我們應(yīng)該如何為位枚舉的成員賦值呢?由于位枚舉成員的值和位運算都直接與二進制相關(guān),所以,我們不妨從二進制的角度去探索一下如何恰當(dāng)?shù)臑槲幻杜e的成員賦值。
試想一下,如果我們能把二進制值的字面特征與位枚舉成員關(guān)聯(lián)起來,使得我們能夠直接從二進制值的字面特征判斷位枚舉成員的存在與否該多好呀!
考察你的Music枚舉,它有4個成員,那么我們把二進制值的數(shù)位設(shè)定為4,即[D][C][B][A]型;并規(guī)定每一個數(shù)位代表該一個枚舉成員,即A代表Music.Jazz、B代表Music.Rock、C代表Music.Country、D代表Music.Classic;那么,某個數(shù)位的值為1就代表其對應(yīng)的枚舉成員存在,為0則不存在?,F(xiàn)在,假如Music的某個變量m的二進制值為0110,我們就可以肯定的說,m中包含著Music.Rock和Music.Country,因其B、C數(shù)位的值均為1。
那么這些跟為位枚舉成員賦值有什么關(guān)系呢?從上面的討論可以知道,Music各個成員的二進制值分別為:Music.Jazz為0001、Music.Rock為0010、Music.Country為0100、Music.Classic為1000。把這些值轉(zhuǎn)換為十六進制值并賦予對應(yīng)的成員:
- // Code #36
- [Flags]
- enum Music
- {
- Jazz = 0x01,
- Rock = 0x02,
- Country = 0x04,
- Classic = 0x08
- }
這樣,你就可以采用我所提到的方法來驗證某個枚舉變量中是否包含著特定的枚舉成員了。
--------------------------------------------------------------------------------
Q:如何把C#枚舉類型轉(zhuǎn)換(解析)成字符串類型?
A:最簡單的方法就是使用System.Enum的
- public override string ToString();
方法,或者把枚舉類型轉(zhuǎn)換為IConvertible接口,再調(diào)用該接口的
- string ToString(IFormatProvider provider);
方法。此時你將得到枚舉成員的字面值的字符串:
- // Code #29
- // See Code #01 for Alignment.
- // See Code #21 for FontStyle.
- static void Main()
- {
- Alignment a = Alignment.Right;
- Console.WriteLine("Alignment is {0}.", a.ToString());
- FontStyle fs = FontStyle.Bold | FontStyle.Underline;
- Console.WriteLine("FontStyle is {0}.", fs.ToString());
- }
- // Output:
- // Alignment is Right.
- // FontStyle is Bold, Underline.
如果你希望輸出枚舉成員的值,那么你可以手動指定格式參數(shù):
- // Code #30
- // See Code #01 for Alignment.
- // See Code #21 for FontStyle.
- static void Main()
- {
- Alignment a = Alignment.Right;
- // Represents Alignment in decimal form.
- Console.WriteLine("Alignment is {0}.", a.ToString("d"));
- // Represents Alignment in hexadecimal without a leading "0x".
- Console.WriteLine("Alignment is {0}.", a.ToString("x"));
- FontStyle fs = FontStyle.Bold | FontStyle.Underline;
- // Represents FontStyle in decimal form.
- Console.WriteLine("FontStyle is {0}.", fs.ToString("d"));
- // Represents FontStyle in hexadecimal without a leading "0x".
- Console.WriteLine("FontStyle is {0}.", fs.ToString("x"));
- }
- // Output:
- // Alignment is 2.
- // Alignment is 00000002.
- // FontStyle is 33.
- // FontStyle is 00000021.
除此之外,你還可以使用System.Enum的
- public static string Format(
- Type enumType,
- object value,
- string format
- );
方法:
- // Code #31
- // See Code #01 for Alignment.
- static void Main()
- {
- Alignment a = Alignment.Right;
- Console.WriteLine(
- "Alignment is 0x{0}.",
- System.Enum.Format(typeof(Alignment), a, "x")
- );
- Console.WriteLine("Alignment is 0x{0:x}.", a);
- }
- // Output:
- // Alignment is 0x00000002.
- // Alignment is 0x00000002.
另外,你還可以通過System.Enum的
- public static string[] GetNames(Type enumType);
來獲取枚舉所有成員的字面值:
- // Code #32
- // See Code #01 for Alignment.
- static void Main()
- {
- string[] names = Enum.GetNames(typeof(Alignment));
- foreach(string name in names)
- Console.WriteLine(name);
- }
- // Output:
- // Left
- // Center
- // Right
--------------------------------------------------------------------------------
Q:如果我得到一個表示枚舉成員的字符串,我如何將其解析為對應(yīng)枚舉類型呢?
A:這時你就需要System.Enum的
- public static object Parse(
- Type enumType,
- string value,
- bool ignoreCase
- );
方法了:
- // Code #33
- // See Code #01 for Alignment.
- // See Code #21 for FontStyle.
- static void Main()
- {
- string name = "Right";
- Alignment a = (Alignment)Enum.Parse(typeof(Alignment), name, false);
- Console.WriteLine(a.ToString());
- string names = "Bold, Italic, Underline";
- FontStyle fs = (FontStyle)Enum.Parse(typeof(FontStyle), names, false);
- Console.WriteLine(fs.ToString());
- }
- // Output:
- // Right
- // Bold, Italic, Underline
--------------------------------------------------------------------------------
Q:枚舉類型為我們編碼提供了巨大的便利,什么情況下我們不應(yīng)該使用枚舉呢?
A:首先你應(yīng)該清楚枚舉類型在編程中充當(dāng)一個什么樣的角色。在我看來,枚舉類型表達了一種穩(wěn)定的分類標(biāo)準(zhǔn)。當(dāng)你查看.NET Framework BCL中的枚舉類型,你會發(fā)現(xiàn)它們幾乎沒有任何改變的可能或者趨勢,表現(xiàn)出一種穩(wěn)定性。所以,當(dāng)你所要表達的分類標(biāo)準(zhǔn)也同樣具備這種穩(wěn)定性時,你就可以考慮枚舉類型了。那么什么情況下不使用枚舉呢?一般說來,當(dāng)分類標(biāo)準(zhǔn)不閉合時——即新的子分類隨時有可能產(chǎn)生或者現(xiàn)有子分類隨時有可能被替換——你就應(yīng)該考慮使用其他的方式來表達了。
下面讓我們來看一個薪酬自動管理系統(tǒng)的一部分,假設(shè)某公司現(xiàn)有雇員種類為:
1. Programmer
2. Salesman
3. Manager
相關(guān)代碼如下:
- // Code #34
- public enum EmployeeKind
- {
- Programmer,
- Salesman,
- Manager
- }
- public class Employee
- {
- public Employee(string name, EmployeeKind kind)
- {
- Kind = kind;
- }
- private string m_Name;
- public string Name
- {
- get { return m_Name; }
- }
- public readonly EmployeeKind Kind;
- public double GetPayment()
- {
- switch(Kind)
- {
- case EmployeeKind.Programmer:
- // Return payment
- case EmployeeKind.Salesman:
- // Return payment
- case EmployeeKind.Manager:
- // Return payment
- }
- }
- }
假如該公司正處于成長期,那么公司的組織結(jié)構(gòu)發(fā)生改變是家常便飯之事。但公司每一次改變組織結(jié)構(gòu),這樣的系統(tǒng)就要經(jīng)歷源代碼修改、重新編譯然后再部署的過程!而且,如果公司實行了新的績效評估方案,并把薪酬計算與績效追蹤掛鉤,那么GetPayment方法將進一步壯大,以至于最終其代碼晦澀難懂。你當(dāng)然可以把GetPayment分割為子方法,但并沒有什么實質(zhì)的改變。再想一想,如果將來公司打算把薪酬系統(tǒng)與銀行掛鉤,提供薪資直接銀行劃撥,那將又會是怎么一番局面呢?
以上就總結(jié)了一些C#枚舉應(yīng)用情景的相關(guān)問答。
【編輯推薦】