小枚舉,大文章

Q:在C#裏,我們如何表達枚舉類型?

A:你可以使用enum關鍵字(keyword)來聲明一個枚舉類型(enum type):

// Code #01
public enum Alignment
{
Left,
Center,
Right
}

--------------------------------------------------------------------------------

Q:枚舉類型是值類型(value type)還是引用類型(reference type)?

A:枚舉類型都是值類型。

--------------------------------------------------------------------------------

Q:System.Enum是枚舉類型麼?

A:不是。

--------------------------------------------------------------------------------

Q:System.Enum與枚舉類型(enum type)有什麼關係?

A:System.Enum是一個抽象類(abstract class),所有枚舉類型都直接繼承自它,當然也同時繼承了它的所有成員。

--------------------------------------------------------------------------------

Q:那麼System.Enum屬於引用類型啦?

A:是的。

--------------------------------------------------------------------------------

Q:既然System.Enum是引用類型,而枚舉類型又是直接繼承自System.Enum的,那爲什麼枚舉類型卻不是引用類型?

A:這種繼承關係是隱式的並由編譯器負責展開,上面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#裏顯式聲明這種繼承關係。

--------------------------------------------------------------------------------

Q:但你好像沒有回答爲什麼枚舉類型繼承自一個引用類型後,卻還是值類型!

A:你知道,所有的值類型都是System.ValueType的後代,枚舉類型也不例外,枚舉類型直接繼承自System.Enum,而System.Enum卻又直接繼承自System.ValueType的,所以,枚舉類型也是System.ValueType的後代。

--------------------------------------------------------------------------------

Q:慢着!從System.ValueType派生出來的類型不都應該是值類型嗎?爲什麼System.Enum會是引用類型?

A:正確的說法應該是“值類型都是System.ValueType的後代”,但System.ValueType的後代不全是值類型,System.Enum就是唯一的特例!在System.ValueType的所有後代中,除了System.Enum之外其它都是值類型。事實上,我們可以在.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:開始頭暈了,究竟枚舉類型、System.Enum、System.ValueType、值類型和引用類型之間存在着什麼樣的關係?

A:簡單的說,

1. 所有枚舉類型(enum type)都是值類型。
2. System.Enum和System.ValueType本身是引用類型。
3. 枚舉類型(enum type)都是隱式的直接繼承自System.Enum,並且這種繼承關係只能由編譯器自動展開。但System.Enum本身不是枚舉類型(enum type)。
4. System.Enum是一個特例,它直接繼承自System.ValueType(參見Code #03),但本身卻是一個引用類型。
好吧,現在來看看下面代碼,你能猜得出它的輸出結果嗎?

// 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.");
}

請別驚訝於程序的運行結果沒有任何輸出!對於第一個判斷,我們很清楚System.Enum並不是枚舉類型。但第二個判斷呢?System.Enum明明繼承自System.ValueType,卻不承認是System.ValueType的後代!這是.NET上的一個特例,恰恰體現出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所實現的三個接口:System.IConvertible、System.IComparable、System.IFormattable。對應的裝箱操作既可以爲隱式的也可以是顯式的。

下面的C#代碼:

// Code #05
// See Code #01 for Alignment.
static void Main()
{
Alignment a = Alignment.Center;

Console.WriteLine(a.ToString());

Console.WriteLine(a);
}

對應的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。

但如果你讓編譯器自動爲你選擇裝箱類型的話,它會優先考慮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
// 調用static void Print(Enum e);
L_0008: call void EnumerationFaq.Program::Print([mscorlib]System.Enum)
L_000d: nop
L_000e: ret
}

--------------------------------------------------------------------------------

Q:我留意到Code #02中的

.field public static literal Aligment Center = int32(0x00000001)

該語句明顯是整數賦值,這是否說明枚舉類型實質上是整數類型?

A:這說明枚舉類型與整數類型的確有一定的關係。事實上,每一個枚舉類型都有與之相對應的整數類型,我們稱該整數類型爲底層類型(underlying type),默認的情況下使用,.NET使用System.Int32。當然,你可以手動將其指定爲其他的整數類型:

// Code #09
public enum Alignment : byte
{
Left,
Center,
Right
}

注意,能被指定爲枚舉的底層類型的只能是如下所列的整數類型:byte, sbyte, short, ushort, int, uint, long, ulong。

--------------------------------------------------------------------------------

Q:爲何我們需要指定枚舉類型的底層類型?

A:你完全可以讓它接受默認的底層類型。請留意Code #08,你完全找不到“Center”這個字眼,然而在C#代碼中,它卻是存在的,爲什麼呢?這是因爲代碼在編譯的時候,編譯器把枚舉類型轉換爲與之對應的底層類型的數值來處理。Code #08的L_0000實際上就是把類型爲System.Int32的數值1推入堆棧,而不是把“Center”推入堆棧。事實上,底層類型說明了如何爲枚舉類型分配空間,不同的底層類型所佔用的資源不同,大概當你在受限系統上進行開發的話,你就可能需要注意一下了。

--------------------------------------------------------------------------------

Q:枚舉成員的值是怎樣規定的?

A:如果你沒有手動指定成員的值的話,從上往下看,各成員的值爲:0, 1, 2, ...。說罷了,就是一個非負整數等差數列,其初值爲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:我們如何獲取枚舉成員的值,無論成員是否被手動賦值?

A:你可以使用System.Enum的

public static Array GetValues(Type enumType);

該方法返回一個包含所有枚舉成員的數組:

// 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:那麼你可以把枚舉轉換爲IConvertible接口,再調用對應的方法:

// 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:一般情況下,使用默認的賦值規則就足夠了,但某些情況下,爲枚舉成員指定一個與實際情況(模型)相符的值可能更有意義,這要視你具體所建的模型而定。

還是讓我們來一個實際的例子:

// 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屬性就通過強類型轉換來獲取枚舉成員的值(也就是購物折扣率),並用於貨款計算。從這裏可以看出,獲取枚舉成員的值還可以通過強類型轉換方式。

--------------------------------------------------------------------------------

Q:既然枚舉類型可以強制轉換爲整數,那麼整數是否也可以強制轉換爲枚舉類型?

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!我們總不能假設人人都那麼守規矩,所以,我們需要System.Enum的

public static bool IsDefined(Type enumType, object value);

現在我們把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方法高效多了。

--------------------------------------------------------------------------------

Q:我定義了一個這樣的枚舉:

// Code #20
public enum FontStyle
{
Bold,
Italic,
Regular,
Strikethrough,
Underline
}

我用它來指定字體的風格,但我遇到了麻煩。你知道,字體可以同時擁有枚舉裏面所列舉的一種或者多種風格,那麼,我如何爲字體同時指定多種風格呢?

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
}

現在,你可以通過按位或運算來爲字體指定多種風格了:

// 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方法是否還能應對呢?

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都不是一個單獨的枚舉成員。但這不是我們所追求的答案,我們希望區別對待fs1和fs2,至少我們不希望fs2中的搗蛋傢伙——(FontStyle)0x0400——在我們的程序中搞破壞!

--------------------------------------------------------------------------------

Q:那麼,我們是否有辦法隔離這些搗蛋鬼呢?

A:Of course!我們同樣可以使用條件判斷語句來處理,但做法將與Code #17和Code #18有所不同。現在我們把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
}
}

我們把枚舉變量與某一特定的位枚舉成員進行按位與運算,若結果不爲0則表明枚舉變量中包含着該位枚舉成員。當然,你也可以爲自己的代碼寫一組這樣的方法:

// 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;
}

留個“作業”吧,知道爲何這樣可以判斷出是否有搗蛋鬼嗎?當然,如果你想到了更好的方法,記住要告訴我喲!

--------------------------------------------------------------------------------

Q:慢着!你那個“我們把枚舉變量與某一特定的位枚舉成員進行按位與運算,若結果不爲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!也正如你所看到的,這種做法其實與位枚舉成員的值是如何被賦予息息相關的。那麼,我們應該如何爲位枚舉的成員賦值呢?由於位枚舉成員的值和位運算都直接與二進制相關,所以,我們不妨從二進制的角度去探索一下如何恰當的爲位枚舉的成員賦值。

試想一下,如果我們能把二進制值的字面特徵與位枚舉成員關聯起來,使得我們能夠直接從二進制值的字面特徵判斷位枚舉成員的存在與否該多好呀!

考察你的Music枚舉,它有4個成員,那麼我們把二進制值的數位設定爲4,即[D][C][B][A]型;並規定每一個數位代表該一個枚舉成員,即A代表Music.Jazz、B代表Music.Rock、C代表Music.Country、D代表Music.Classic;那麼,某個數位的值爲1就代表其對應的枚舉成員存在,爲0則不存在。現在,假如Music的某個變量m的二進制值爲0110,我們就可以肯定的說,m中包含着Music.Rock和Music.Country,因其B、C數位的值均爲1。

那麼這些跟爲位枚舉成員賦值有什麼關係呢?從上面的討論可以知道,Music各個成員的二進制值分別爲:Music.Jazz爲0001、Music.Rock爲0010、Music.Country爲0100、Music.Classic爲1000。把這些值轉換爲十六進制值並賦予對應的成員:

// Code #36
[Flags]
enum Music
{
Jazz = 0x01,
Rock = 0x02,
Country = 0x04,
Classic = 0x08
}

這樣,你就可以採用我所提到的方法來驗證某個枚舉變量中是否包含着特定的枚舉成員了。

--------------------------------------------------------------------------------

Q:如何把枚舉類型轉換(解析)成字符串類型?

A:最簡單的方法就是使用System.Enum的

public override string ToString();

方法,或者把枚舉類型轉換爲IConvertible接口,再調用該接口的

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.

如果你希望輸出枚舉成員的值,那麼你可以手動指定格式參數:

// 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:如果我得到一個表示枚舉成員的字符串,我如何將其解析爲對應枚舉類型呢?

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:枚舉類型爲我們編碼提供了巨大的便利,什麼情況下我們不應該使用枚舉呢?

A:首先你應該清楚枚舉類型在編程中充當一個什麼樣的角色。在我看來,枚舉類型表達了一種穩定的分類標準。當你查看.NET Framework BCL中的枚舉類型,你會發現它們幾乎沒有任何改變的可能或者趨勢,表現出一種穩定性。所以,當你所要表達的分類標準也同樣具備這種穩定性時,你就可以考慮枚舉類型了。那麼什麼情況下不使用枚舉呢?一般說來,當分類標準不閉合時——即新的子分類隨時有可能產生或者現有子分類隨時有可能被替換——你就應該考慮使用其他的方式來表達了。

下面讓我們來看一個薪酬自動管理系統的一部分,假設某公司現有僱員種類爲:

1. Programmer
2. Salesman
3. Manager
相關代碼如下:

// 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
}
}
}

假如該公司正處於成長期,那麼公司的組織結構發生改變是家常便飯之事。但公司每一次改變組織結構,這樣的系統就要經歷源代碼修改、重新編譯然後再部署的過程!而且,如果公司實行了新的績效評估方案,並把薪酬計算與績效追蹤掛鉤,那麼GetPayment方法將進一步壯大,以至於最終其代碼晦澀難懂。你當然可以把GetPayment分割爲子方法,但並沒有什麼實質的改變。再想一想,如果將來公司打算把薪酬系統與銀行掛鉤,提供薪資直接銀行劃撥,那將又會是怎麼一番局面呢?
發佈了45 篇原創文章 · 獲贊 6 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章