您的位置:首页 > 编程语言 > C#

关于枚举的种种 (Enumeration FAQ) [C#, IL, BCL]

2009-02-02 03:34 399 查看
原文 http://www.cnblogs.com/allenlooplee/archive/2004/12/19/70230.html
Q:在C#里,我们如何表达枚举类型?

A:你可以使用enum关键字(keyword)来声明一个枚举类型(enum type):

// Code #01

public enum Alignment

// Code #02

.class public auto ansi sealed Aligment

extends [mscorlib]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:开始头晕了 // Code #04

static void Main()

// Code #05

// See Code #01 for Alignment.

static void Main()

// Code #06

.method private hidebysig static void Main() cil managed

// Code #07

// See Code #01 for Alignment.

class Program

// Code #08

.method private hidebysig static void Main(string[] args) cil managed

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

该语句明显是整数赋值,这是否说明枚举类型实质上是整数类型?

A:这说明枚举类型与整数类型的确有一定的关系。事实上,每一个枚举类型都有与之相对应的整数类型,我们称该整数类型为底层类型(underlying type),默认的情况下使用,.NET使用System.Int32。当然,你可以手动将其指定为其他的整数类型:

// Code #09

public enum Alignment : byte

// Code #10

public enum Alignment

// Code #11

public enum DriveType : sbyte

public static Array GetValues(Type enumType);

该方法返回一个包含所有枚举成员的数组:

// Code #12

// See Code #01 for Alignment.

public static void Main()

// 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()

// Output:

// The value of Alignment.Center is 1.

Q:为什么需要手动指定枚举成员的值?

A:一般情况下,使用默认的赋值规则就足够了,但某些情况下,为枚举成员指定一个与实际情况(模型)相符的值可能更有意义,这要视你具体所建的模型而定。

还是让我们来一个实际的例子:

// Code #13

public enum CustomerKind

public class Customer

// Code #14

// See Code #01 for Alignment.

Alignment a = (Alignment)1;

但这种机制可能使你遇到一些麻烦:

// Code #15

// See Code #01 for Alignment.

class Program

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

现在我们把Code #15的Foo方法改进一下:

// Code #16

// See Code #01 for Alignment.

static void Foo(Alignment a)

// Code #17

// See Code #01 for Alignment.

static void Foo(Alignment a)

// Code #18

// See Code #01 for Alignment.

static void Foo(Alignment a)

// Code #19

// See Code #01 for Alignment.

public static bool IsAlignment(Alignment a)

// Code #20

public enum FontStyle

// Code #21

// I am using the FlagsAttribute to identify a bit flags.

[Flags]

public enum FontStyle

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

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

// Code #26

// See Code #21 for FontStyle.

static bool ContainsBold(FontStyle fs)

static bool ContainsItalic(FontStyle fs)

// Other similar methods continue here

又或者你可以写一个这样的方法:

// Code #27

// See Code #21 for FontStyle.

static bool ContainsMember(FontStyle fs, FontStyle menber)

// Code #28

// See Code #21 for FontStyle.

static bool ContainsPranksters(FontStyle fs)

// Code #35

[Flags]

enum Music

static void Main()

// Code #36

[Flags]

enum Music

public override string ToString();

方法,或者把枚举类型转换为IConvertible接口,再调用该接口的

string ToString(IFormatProvider provider);

方法。此时你将得到枚举成员的字面值的字符串:

// Code #29

// See Code #01 for Alignment.

// See Code #21 for FontStyle.

static void Main()

// Output:

// Alignment is Right.

// FontStyle is Bold, Underline.

如果你希望输出枚举成员的值,那么你可以手动指定格式参数:

// Code #30

// See Code #01 for Alignment.

// See Code #21 for FontStyle.

static void Main()

// 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()

// 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()

// 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()

// Output:

// Right

// Bold, Italic, Underline

Q:枚举类型为我们编码提供了巨大的便利,什么情况下我们不应该使用枚举呢?

A:首先你应该清楚枚举类型在编程中充当一个什么样的角色。在我看来,枚举类型表达了一种稳定的分类标准。当你查看.NET Framework BCL中的枚举类型,你会发现它们几乎没有任何改变的可能或者趋势,表现出一种稳定性。所以,当你所要表达的分类标准也同样具备这种稳定性时,你就可以考虑枚举类型了。那么什么情况下不使用枚举呢?一般说来,当分类标准不闭合时——即新的子分类随时有可能产生或者现有子分类随时有可能被替换——你就应该考虑使用其他的方式来表达了。

下面让我们来看一个薪酬自动管理系统的一部分,假设某公司现有雇员种类为:

1. Programmer

2. Salesman

3. Manager

相关代码如下:

// Code #34

public enum EmployeeKind

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分割为子方法,但并没有什么实质的改变。再想一想,如果将来公司打算把薪酬系统与银行挂钩,提供薪资直接银行划拨,那将又会是怎么一番局面呢?

好吧,我承认我不太愿意接手维护这样的系统

,但讨论如何改进这个系统会使本文进一步壮大,以至于最终其内容晦涩难懂

,不过你应该可以从我的《今天你多态了吗?》中找到一些改进灵感。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: