关于枚举的种种

摘要:
A:这种继承关系是隐式的并由编译器负责展开,上面Code#1的Alignment枚举被展开后的IL代码如下:{.fieldpublicstaticliteralAligmentLeft=int32.fieldpublicstaticliteralAligmentCenter=int32.fieldpublicstaticliteralAligmentRight=int32.fieldpublicspecialnamertspecialnameint32value__}从声明中,你可以看到Aligment的确是继承自System.Enum的,只是你不能在C#里显式声明这种继承关系。对于第一个判断,我们很清楚System.Enum并不是枚举类型。注意:在.NET1.1上,枚举类型只能被装箱到System.Enum、System.ValueType、System.Object;而在.NET2.0上,枚举类型还能被装箱到System.Enum所实现的三个接口:System.IConvertible、System.IComparable、System.IFormattable。

转载自:http://www.cnblogs.com/allenlooplee/archive/2004/12/19/70230.html

Q:在C#里,我们如何表达枚举类型?

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

关于枚举的种种第1张{
关于枚举的种种第2张Left,
关于枚举的种种第2张Center,
关于枚举的种种第2张Right
关于枚举的种种第5张}

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的,那为什么枚举类型却不是引用类型?关于枚举的种种第6张

A:这种继承关系是隐式的并由编译器负责展开,上面Code #1的Alignment枚举被展开后的IL代码如下:

关于枚举的种种第1张{
关于枚举的种种第2张.field
publicstaticliteralAligmentLeft=int32(0x00000000)
关于枚举的种种第2张.field
publicstaticliteralAligmentCenter=int32(0x00000001)
关于枚举的种种第2张.field
publicstaticliteralAligmentRight=int32(0x00000002)
关于枚举的种种第2张
关于枚举的种种第2张.field
publicspecialnamertspecialnameint32value__
关于枚举的种种第5张}

从声明中,你可以看到Aligment的确是继承自System.Enum的,只是你不能在C#里显式声明这种继承关系。


Q:但你好像没有回答为什么枚举类型继承自一个引用类型后,却还是值类型!

A:你知道,所有的值类型都是System.ValueType的后代,枚举类型也不例外,枚举类型直接继承自System.Enum,而System.Enum却又直接继承自System.ValueType的,所以,枚举类型也是System.ValueType的后代。


Q:慢着!从System.ValueType派生出来的类型不都应该是值类型吗?为什么System.Enum会是引用类型?关于枚举的种种第6张

A:正确的说法应该是“值类型都是System.ValueType的后代”,但System.ValueType的后代不全是值类型,System.Enum就是唯一的特例!在System.ValueType的所有后代中,除了System.Enum之外其它都是值类型。事实上,我们可以在.NET的源代码中找到System.Enum的声明:

关于枚举的种种第15张publicabstractclassEnum:ValueType,IComparable,IFormattable,IConvertible

请注意,.NET Framework SDK v2.0.3600.0 Documentation中的Enum声明是错的:

publicabstractstruct Enum : IComparable, IFormattable, IConvertible


Q:开始头晕了关于枚举的种种第16张,究竟枚举类型、System.Enum、System.ValueType、值类型和引用类型之间存在着什么样的关系?关于枚举的种种第17张

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),但本身却是一个引用类型。

好吧,现在来看看下面代码,你能猜得出它的输出结果吗?

关于枚举的种种第1张{
关于枚举的种种第2张Typet
=typeof(System.Enum);
关于枚举的种种第2张
关于枚举的种种第2张
if(t.IsEnum)
关于枚举的种种第2张Console.WriteLine(
"I'menumtype.");
关于枚举的种种第2张
关于枚举的种种第2张
if(t.IsValueType)
关于枚举的种种第2张Console.WriteLine(
"I'mvaluetype.");
关于枚举的种种第5张}

请别惊讶于程序的运行结果没有任何输出!对于第一个判断,我们很清楚System.Enum并不是枚举类型。但第二个判断呢?System.Enum明明继承自System.ValueType,却不承认是System.ValueType的后代!这是.NET上的一个特例,恰恰体现出System.Enum是特殊性。关于枚举的种种第27张


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#代码:

关于枚举的种种第1张{
关于枚举的种种第2张Alignmenta
=Alignment.Center;
关于枚举的种种第2张
关于枚举的种种第2张Console.WriteLine(a.ToString());
关于枚举的种种第2张
关于枚举的种种第2张Console.WriteLine(a);
关于枚举的种种第5张}

对应的IL代码是:

关于枚举的种种第1张{
关于枚举的种种第2张
.entrypoint
关于枚举的种种第2张
//CodeSize:32byte(s)
关于枚举的种种第2张
.maxstack1
关于枚举的种种第2张
.locals(
关于枚举的种种第2张EnumerationFaq.Alignmentalignment1)
关于枚举的种种第2张L_0000:ldc.i4.1
关于枚举的种种第2张L_0001:stloc.0
关于枚举的种种第2张L_0002:ldloc.0
关于枚举的种种第2张L_0003:boxEnumerationFaq.Alignment
关于枚举的种种第2张L_0008:callinstance
string[mscorlib]System.Enum::ToString()
关于枚举的种种第2张L_000d:call
void[mscorlib]System.Console::WriteLine(string)
关于枚举的种种第2张L_0012:nop
关于枚举的种种第2张L_0013:ldloc.0
关于枚举的种种第2张L_0014:boxEnumerationFaq.Alignment
关于枚举的种种第2张L_0019:call
void[mscorlib]System.Console::WriteLine(object)
关于枚举的种种第2张L_001e:nop
关于枚举的种种第2张L_001f:ret
关于枚举的种种第5张}

从IL代码中我们可以看到枚举类型被装箱两次。第一次(L_0003)被装箱成System.Enum,而第二次(L_0014)就被装箱成System.Object。

但如果你让编译器自动为你选择装箱类型的话,它会优先考虑System.Enum:

关于枚举的种种第1张{
关于枚举的种种第2张
staticvoidMain()
关于枚举的种种第56张关于枚举的种种第57张
关于枚举的种种第1张{
关于枚举的种种第2张Alignmenta
=Alignment.Center;
关于枚举的种种第2张
关于枚举的种种第2张Print(a);
关于枚举的种种第62张}

关于枚举的种种第2张
关于枚举的种种第2张
staticvoidPrint(IConvertiblec)
关于枚举的种种第56张关于枚举的种种第57张
关于枚举的种种第1张{
关于枚举的种种第2张Console.WriteLine(c);
关于枚举的种种第62张}

关于枚举的种种第2张
关于枚举的种种第2张
staticvoidPrint(IFormattablef)
关于枚举的种种第56张关于枚举的种种第57张
关于枚举的种种第1张{
关于枚举的种种第2张Console.WriteLine(f);
关于枚举的种种第62张}

关于枚举的种种第2张
关于枚举的种种第2张
staticvoidPrint(IComparablec)
关于枚举的种种第56张关于枚举的种种第57张
关于枚举的种种第1张{
关于枚举的种种第2张Console.WriteLine(c);
关于枚举的种种第62张}

关于枚举的种种第2张
关于枚举的种种第2张
staticvoidPrint(Objecto)
关于枚举的种种第56张关于枚举的种种第57张
关于枚举的种种第1张{
关于枚举的种种第2张Console.WriteLine(o);
关于枚举的种种第62张}

关于枚举的种种第2张
关于枚举的种种第2张
staticvoidPrint(ValueTypev)
关于枚举的种种第56张关于枚举的种种第57张
关于枚举的种种第1张{
关于枚举的种种第2张Console.WriteLine(v);
关于枚举的种种第62张}

关于枚举的种种第2张
关于枚举的种种第2张
staticvoidPrint(Enume)
关于枚举的种种第56张关于枚举的种种第57张
关于枚举的种种第1张{
关于枚举的种种第2张Console.WriteLine(e);
关于枚举的种种第62张}

关于枚举的种种第5张}

上面的代码将被编译成如下的IL:

关于枚举的种种第1张{
关于枚举的种种第2张.entrypoint
关于枚举的种种第2张
//CodeSize:15byte(s)
关于枚举的种种第2张
.maxstack1
关于枚举的种种第2张.locals(
关于枚举的种种第2张EnumerationFaq.Alignmentalignment1)
关于枚举的种种第2张L_0000:ldc.i4.
1
关于枚举的种种第2张L_0001:stloc.
0
关于枚举的种种第2张L_0002:ldloc.
0
关于枚举的种种第2张L_0003:boxEnumerationFaq.Alignment
关于枚举的种种第2张
//调用staticvoidPrint(Enume);
关于枚举的种种第2张
L_0008:callvoidEnumerationFaq.Program::Print([mscorlib]System.Enum)
关于枚举的种种第2张L_000d:nop
关于枚举的种种第2张L_000e:ret
关于枚举的种种第5张}

Q:我留意到Code #02中的

关于枚举的种种第15张.fieldpublicstaticliteralAligmentCenter=int32(0x00000001)

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

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

关于枚举的种种第1张{
关于枚举的种种第2张Left,
关于枚举的种种第2张Center,
关于枚举的种种第2张Right
关于枚举的种种第5张}

注意,能被指定为枚举的底层类型的只能是如下所列的整数类型: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。例如:

关于枚举的种种第1张{
关于枚举的种种第2张Left,
//0
关于枚举的种种第2张
Center,//1
关于枚举的种种第2张
Right//2
关于枚举的种种第5张
}

Q:如果我有手动指定某些成员的值呢?

A:那么被赋值的成员的值就是你所指定的值。当然,无论你是否手动指定枚举成员的值,递增步长都不会变,总是为1。为了测试你是否理解,请说出下面枚举个成员的值以及你的判断理由(请用人脑而不是电脑来运行以下代码关于枚举的种种第132张):

关于枚举的种种第1张{
关于枚举的种种第2张CDRom,
关于枚举的种种第2张Fixed
=-2,
关于枚举的种种第2张Network,
关于枚举的种种第2张NoRootDirectory
=-1,
关于枚举的种种第2张Ram,
关于枚举的种种第2张Removable
=Network*NoRootDirectory,
关于枚举的种种第2张Unknown
关于枚举的种种第5张}

Q:我们如何获取枚举成员的值,无论成员是否被手动赋值?

A:你可以使用System.Enum的

关于枚举的种种第15张publicstaticArrayGetValues(TypeenumType);

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

关于枚举的种种第1张{
关于枚举的种种第2张Alignment[]alignments
=(Alignment[])Enum.GetValues(typeof(Alignment));
关于枚举的种种第2张Console.WriteLine(
"WannaseethevaluesofAlignment'smenbers?");
关于枚举的种种第2张
foreach(Alignmentainalignments)
关于枚举的种种第2张Console.WriteLine(
"{0:G}={0:D}",a);
关于枚举的种种第5张}

关于枚举的种种第15张
关于枚举的种种第15张
//Output:
关于枚举的种种第15张
//WannaseethevaluesofAlignment'smenbers?
关于枚举的种种第15张
//Left=0
关于枚举的种种第15张
//Center=1
关于枚举的种种第15张
//Right=2

Q:如果我只需要其中某些枚举成员的值呢?

A:那么你可以把枚举转换为IConvertible接口,再调用对应的方法:

关于枚举的种种第1张{
关于枚举的种种第2张IConvertibleic
=(IConvertible)Alignment.Center;
关于枚举的种种第2张
inti=ic.ToInt32(null);
关于枚举的种种第2张Console.WriteLine(
"ThevalueofAlignment.Centeris{0}.",i);
关于枚举的种种第5张}

关于枚举的种种第15张
关于枚举的种种第15张
//Output:
关于枚举的种种第15张
//ThevalueofAlignment.Centeris1.

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

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

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

关于枚举的种种第1张{
关于枚举的种种第2张
publicreadonlyCustomerKindKind;
关于枚举的种种第2张
关于枚举的种种第2张
privatedoublem_Payment;
关于枚举的种种第2张
publicdoublePayment
关于枚举的种种第56张关于枚举的种种第57张
关于枚举的种种第1张{
关于枚举的种种第2张
returnm_Payment*(int)Kind/100;
关于枚举的种种第62张}

关于枚举的种种第2张
关于枚举的种种第2张
//Codehere关于枚举的种种第1张
关于枚举的种种第5张
}

我为枚举CustomerKind的每个成员都赋了一个特定的值,该值其实就是顾客购物折扣百分率。而在Customer类中,Payment属性就通过强类型转换来获取枚举成员的值(也就是购物折扣率),并用于货款计算。从这里可以看出,获取枚举成员的值还可以通过强类型转换方式。


Q:既然枚举类型可以强制转换为整数,那么整数是否也可以强制转换为枚举类型?

A:答案是肯定的。

关于枚举的种种第15张//Code#14
关于枚举的种种第15张
//SeeCode#01forAlignment.
关于枚举的种种第15张
Alignmenta=(Alignment)1;

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

关于枚举的种种第1张{
关于枚举的种种第2张
staticvoidMain()
关于枚举的种种第56张关于枚举的种种第57张
关于枚举的种种第1张{
关于枚举的种种第2张Foo((Alignment)
12345);
关于枚举的种种第62张}

关于枚举的种种第2张
关于枚举的种种第2张
staticvoidFoo(Alignmenta)
关于枚举的种种第56张关于枚举的种种第57张
关于枚举的种种第1张{
关于枚举的种种第2张
//Codehere关于枚举的种种第1张
关于枚举的种种第62张
}

关于枚举的种种第5张}

你无法避免有人进行这样的恶作剧!!关于枚举的种种第196张


Q:那么是否有办法对付这些恶作剧的人?

A:Sure!我们总不能假设人人都那么守规矩,所以,我们需要System.Enum的

关于枚举的种种第15张publicstaticboolIsDefined(TypeenumType,objectvalue);

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

关于枚举的种种第1张{
关于枚举的种种第2张
if(!Enum.IsDefined(typeof(Alignment),a))
关于枚举的种种第2张
thrownewArgumentException("DONOTMAKEMISCHIEF!");
关于枚举的种种第2张
关于枚举的种种第2张
//Codehere关于枚举的种种第1张
关于枚举的种种第5张
}

这样,恶作剧的人将会收到一个警告(异常消息)关于枚举的种种第205张。当然,我们不排除有人是由于一时大意才造成这样的“恶作剧”,那么IsDefined方法同样可以帮助你处理好这些情况。


Q:我认为我们还可以使用条件判断语句来处理这种情况:

关于枚举的种种第1张{
关于枚举的种种第2张
if(a!=Alignment.Left&&
关于枚举的种种第2张a
!=Alignment.Center&&
关于枚举的种种第2张a
!=Alignment.Right)
关于枚举的种种第2张
thrownewArgumentException("DONOTMAKEMISCHIEF!");
关于枚举的种种第2张
关于枚举的种种第2张
//Codehere关于枚举的种种第1张
关于枚举的种种第5张
}

或者

关于枚举的种种第1张{
关于枚举的种种第2张
switch(a)
关于枚举的种种第56张关于枚举的种种第57张
关于枚举的种种第1张{
关于枚举的种种第2张
caseAlignment.Left:
关于枚举的种种第2张Console.WriteLine(
"Cool~");
关于枚举的种种第2张
break;
关于枚举的种种第2张
caseAlignment.Center:
关于枚举的种种第2张Console.WriteLine(
"Well~");
关于枚举的种种第2张
break;
关于枚举的种种第2张
caseAlignment.Right:
关于枚举的种种第2张Console.WriteLine(
"Good~");
关于枚举的种种第2张
break;
关于枚举的种种第2张
default:
关于枚举的种种第2张Console.WriteLine(
"DONOTMAKEMISCHIEF!");
关于枚举的种种第2张
break;
关于枚举的种种第62张}

关于枚举的种种第5张}

A:你绝对可以这样做!关于枚举的种种第234张事实上,如果你处于以下情况之一的话:

  • 1. Alignment枚举代码不会被修改
  • 2. 你不希望使用Alignment枚举新增的特性

那么我会推荐使用你的处理方式。而且,你还可以为自己的代码定义一个这样的方法:

关于枚举的种种第1张{
关于枚举的种种第2张
switch(a)
关于枚举的种种第56张关于枚举的种种第57张
关于枚举的种种第1张{
关于枚举的种种第2张
caseAlignment.Left:
关于枚举的种种第2张
returntrue;
关于枚举的种种第2张
caseAlignment.Center:
关于枚举的种种第2张
returntrue;
关于枚举的种种第2张
caseAlignment.Right:
关于枚举的种种第2张
returntrue;
关于枚举的种种第2张
default:
关于枚举的种种第2张
returnfalse;
关于枚举的种种第62张}

关于枚举的种种第5张}

这个方法比起IsDefine方法高效多了。


Q:我定义了一个这样的枚举:

关于枚举的种种第1张{
关于枚举的种种第2张Bold,
关于枚举的种种第2张Italic,
关于枚举的种种第2张Regular,
关于枚举的种种第2张Strikethrough,
关于枚举的种种第2张Underline
关于枚举的种种第5张}

我用它来指定字体的风格,但我遇到了麻烦。你知道,字体可以同时拥有枚举里面所列举的一种或者多种风格,那么,我如何为字体同时指定多种风格呢?

A:这个时候你就需要位枚举(Bit Flags),把Code #20修改一下:

关于枚举的种种第1张{
关于枚举的种种第2张Bold
=0x0001,
关于枚举的种种第2张Italic
=0x0002,
关于枚举的种种第2张Regular
=0x0004,
关于枚举的种种第2张Strikethrough
=0x0010,
关于枚举的种种第2张Underline
=0x0020
关于枚举的种种第5张}

现在,你可以通过按位或运算来为字体指定多种风格了:

关于枚举的种种第15张//Code#22
关于枚举的种种第15张
//SeeCode#21forFontStyle.
关于枚举的种种第15张
Fontf=newFont(
关于枚举的种种第15张FontFamily.GenericSansSerif,
关于枚举的种种第15张
12.0F,
关于枚举的种种第15张FontStyle.Italic
|FontStyle.Underline
关于枚举的种种第15张);

Q:位枚举同样存在类似于Code #15的恶作剧吧?

A:是的,例如:

关于枚举的种种第1张{
关于枚举的种种第2张
staticvoidMain()
关于枚举的种种第56张关于枚举的种种第57张
关于枚举的种种第1张{
关于枚举的种种第2张Bar(FontStyle.Regular
|(FontStyle)0x0400);
关于枚举的种种第62张}

关于枚举的种种第2张
关于枚举的种种第2张
staticvoidBar(FontStylefs)
关于枚举的种种第56张关于枚举的种种第57张
关于枚举的种种第1张{
关于枚举的种种第2张
//Codehere关于枚举的种种第1张
关于枚举的种种第62张
}

关于枚举的种种第5张}

Q:那么,System.Enum.IsDefine方法是否还能应对呢?

A:不能。位枚举成员并不具备排他性,多个成员可以通过按位或运算组合起来。而System.Enum.IsDefine方法只能判断枚举变量的值是否为某一已定义的枚举成员。请看如下代码:

关于枚举的种种第15张//Code#24
关于枚举的种种第15张
//SeeCode#21forFontStyle.
关于枚举的种种第15张
FontStylefs1=FontStyle.Bold|FontStyle.Italic|FontStyle.Underline;
关于枚举的种种第15张Console.WriteLine(Enum.IsDefine(
typeof(FontStyle),fs1));
关于枚举的种种第15张
关于枚举的种种第15张FontStylefs2
=FontStyle.Regular|(FontStyle)0x0400;
关于枚举的种种第15张Console.WriteLine(Enum.IsDefine(
typeof(FontStyle),fs2));
关于枚举的种种第15张
关于枚举的种种第15张
//Output:
关于枚举的种种第15张
//false
关于枚举的种种第15张
//false

我们对代码的输出毫无疑问,因为fs1和fs2都不是一个单独的枚举成员。但这不是我们所追求的答案,我们希望区别对待fs1和fs2,至少我们不希望fs2中的捣蛋家伙——(FontStyle)0x0400——在我们的程序中搞破坏!


Q:那么,我们是否有办法隔离这些捣蛋鬼呢?

A:Of course!我们同样可以使用条件判断语句来处理,但做法将与Code #17和Code #18有所不同。现在我们把Code #23改进如下:

关于枚举的种种第1张{
关于枚举的种种第2张
staticvoidMain()
关于枚举的种种第56张关于枚举的种种第57张
关于枚举的种种第1张{
关于枚举的种种第2张Bar(FontStyle.Regular
|(FontStyle)0x0400);
关于枚举的种种第62张}

关于枚举的种种第2张
关于枚举的种种第2张
staticvoidBar(FontStylefs)
关于枚举的种种第56张关于枚举的种种第57张
关于枚举的种种第1张{
关于枚举的种种第2张
if((fs&FontStyle.Bold)!=0)
关于枚举的种种第56张关于枚举的种种第57张
关于枚举的种种第1张{
关于枚举的种种第2张
//Dosomethingassociatedwithbold关于枚举的种种第1张
关于枚举的种种第62张
}

关于枚举的种种第2张
关于枚举的种种第2张
if((fs&FontStyle.Italic)!=0)
关于枚举的种种第56张关于枚举的种种第57张
关于枚举的种种第1张{
关于枚举的种种第2张
//Dosomethingassociatedwithitalic关于枚举的种种第1张
关于枚举的种种第62张
}

关于枚举的种种第2张
关于枚举的种种第2张
//Otherconditionalcodecontinueshere关于枚举的种种第1张
关于枚举的种种第62张
}

关于枚举的种种第5张}

我们把枚举变量与某一特定的位枚举成员进行按位与运算,若结果不为0则表明枚举变量中包含着该位枚举成员。当然,你也可以为自己的代码写一组这样的方法:

关于枚举的种种第1张{
关于枚举的种种第2张
if((fs&FontStyle.Italic)!=0)
关于枚举的种种第2张
returntrue;
关于枚举的种种第2张
else
关于枚举的种种第2张
returnfalse;
关于枚举的种种第5张}

关于枚举的种种第15张
关于枚举的种种第15张
//Othersimilarmethodscontinuehere关于枚举的种种第1张

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

关于枚举的种种第1张{
关于枚举的种种第2张
if((fs&member)!=0)
关于枚举的种种第2张
returntrue;
关于枚举的种种第2张
else
关于枚举的种种第2张
returnfalse;
关于枚举的种种第5张}

如果你只希望判断某一个枚举变量里面是否包含捣蛋鬼,你可以写一个这样的方法:

关于枚举的种种第1张{
关于枚举的种种第2张
if((fs<FontStyle.Bold)||(fs>(FontStyle)0x0037))
关于枚举的种种第2张
returntrue;
关于枚举的种种第2张
关于枚举的种种第2张
if(fs==(FontStyle)0x0008||
关于枚举的种种第2张fs==(FontStyle)0x0009||
关于枚举的种种第2张fs==(FontStyle)0x0018||
关于枚举的种种第2张fs==(FontStyle)0x0019||
关于枚举的种种第2张fs==(FontStyle)0x0028||
关于枚举的种种第2张fs==(FontStyle)0x0029)
关于枚举的种种第2张
returntrue;
关于枚举的种种第2张
关于枚举的种种第2张
returnfalse;
关于枚举的种种第5张}

留个“作业”吧,知道为何这样可以判断出是否有捣蛋鬼吗?当然,如果你想到了更好的方法,记住要告诉我哟!关于枚举的种种第27张


Q:慢着!你那个“我们把枚举变量与某一特定的位枚举成员进行按位与运算,若结果不为0则表明枚举变量中包含着该位枚举成员”,在以下的情况显然不成立的:

关于枚举的种种第1张{
关于枚举的种种第2张Musicm
=Music.Rock|Music.Jazz;
关于枚举的种种第2张
intr=(int)(m&Music.Classic);
关于枚举的种种第2张Console.WriteLine(r);
关于枚举的种种第5张}

该代码的输出恰恰就为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。把这些值转换为十六进制值并赋予对应的成员:

关于枚举的种种第1张{
关于枚举的种种第2张Jazz
=0x01,
关于枚举的种种第2张Rock
=0x02,
关于枚举的种种第2张Country
=0x04,
关于枚举的种种第2张Classic
=0x08
关于枚举的种种第5张}

这样,你就可以采用我所提到的方法来验证某个枚举变量中是否包含着特定的枚举成员了。


Q:如何把枚举类型转换(解析)成字符串类型?

A:最简单的方法就是使用System.Enum的

关于枚举的种种第15张publicoverridestringToString();

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

关于枚举的种种第15张stringToString(IFormatProviderprovider);

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

关于枚举的种种第1张{
关于枚举的种种第2张Alignmenta=Alignment.Right;
关于枚举的种种第2张Console.WriteLine("Alignmentis{0}.",a.ToString());
关于枚举的种种第2张
关于枚举的种种第2张FontStylefs=FontStyle.Bold|FontStyle.Underline;
关于枚举的种种第2张Console.WriteLine("FontStyleis{0}.",fs.ToString());
关于枚举的种种第5张}

关于枚举的种种第15张
关于枚举的种种第15张
//Output:
关于枚举的种种第15张//AlignmentisRight.
关于枚举的种种第15张//FontStyleisBold,Underline.

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

关于枚举的种种第1张{
关于枚举的种种第2张Alignmenta=Alignment.Right;
关于枚举的种种第2张
//RepresentsAlignmentindecimalform.
关于枚举的种种第2张
Console.WriteLine("Alignmentis{0}.",a.ToString("d"));
关于枚举的种种第2张
//RepresentsAlignmentinhexadecimalwithoutaleading"0x".
关于枚举的种种第2张
Console.WriteLine("Alignmentis{0}.",a.ToString("x"));
关于枚举的种种第2张
关于枚举的种种第2张FontStylefs=FontStyle.Bold|FontStyle.Underline;
关于枚举的种种第2张
//RepresentsFontStyleindecimalform.
关于枚举的种种第2张
Console.WriteLine("FontStyleis{0}.",fs.ToString("d"));
关于枚举的种种第2张
//RepresentsFontStyleinhexadecimalwithoutaleading"0x".
关于枚举的种种第2张
Console.WriteLine("FontStyleis{0}.",fs.ToString("x"));
关于枚举的种种第5张}

关于枚举的种种第15张
关于枚举的种种第15张
//Output:
关于枚举的种种第15张//Alignmentis2.
关于枚举的种种第15张//Alignmentis00000002.
关于枚举的种种第15张//FontStyleis33.
关于枚举的种种第15张//FontStyleis00000021.

除此之外,你还可以使用System.Enum的

关于枚举的种种第15张publicstaticstringFormat(
关于枚举的种种第15张TypeenumType,
关于枚举的种种第15张
objectvalue,
关于枚举的种种第15张
stringformat
关于枚举的种种第15张);

方法:

关于枚举的种种第1张{
关于枚举的种种第2张Alignmenta=Alignment.Right;
关于枚举的种种第2张Console.WriteLine(
关于枚举的种种第2张"Alignmentis0x{0}.",
关于枚举的种种第2张System.Enum.Format(
typeof(Alignment),a,"x")
关于枚举的种种第2张);
关于枚举的种种第2张Console.WriteLine("Alignmentis0x{0:x}.",a);
关于枚举的种种第5张}

关于枚举的种种第15张
关于枚举的种种第15张
//Output:
关于枚举的种种第15张//Alignmentis0x00000002.
关于枚举的种种第15张//Alignmentis0x00000002.

另外,你还可以通过System.Enum的

关于枚举的种种第15张publicstaticstring[]GetNames(TypeenumType);

来获取枚举所有成员的字面值:

关于枚举的种种第1张{
关于枚举的种种第2张
string[]names=Enum.GetNames(typeof(Alignment));
关于枚举的种种第2张
foreach(stringnameinnames)
关于枚举的种种第2张Console.WriteLine(name);
关于枚举的种种第5张}

关于枚举的种种第15张
关于枚举的种种第15张
//Output:
关于枚举的种种第15张//Left
关于枚举的种种第15张//Center
关于枚举的种种第15张//Right

Q:如果我得到一个表示枚举成员的字符串,我如何将其解析为对应枚举类型呢?

A:这时你就需要System.Enum的

关于枚举的种种第15张publicstaticobjectParse(
关于枚举的种种第15张TypeenumType,
关于枚举的种种第15张
stringvalue,
关于枚举的种种第15张
boolignoreCase
关于枚举的种种第15张);

方法了:

关于枚举的种种第1张{
关于枚举的种种第2张
stringname="Right";
关于枚举的种种第2张Alignmenta=(Alignment)Enum.Parse(
typeof(Alignment),name,false);
关于枚举的种种第2张
关于枚举的种种第2张Console.WriteLine(a.ToString());
关于枚举的种种第2张
关于枚举的种种第2张
stringnames="Bold,Italic,Underline";
关于枚举的种种第2张FontStylefs=(FontStyle)Enum.Parse(
typeof(FontStyle),names,false);
关于枚举的种种第2张
关于枚举的种种第2张Console.WriteLine(fs.ToString());
关于枚举的种种第5张}

关于枚举的种种第15张
关于枚举的种种第15张
//Output:
关于枚举的种种第15张//Right
关于枚举的种种第15张//Bold,Italic,Underline

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

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

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

  • 1. Programmer
  • 2. Salesman
  • 3. Manager

相关代码如下:

关于枚举的种种第1张{
关于枚举的种种第2张
publicEmployee(stringname,EmployeeKindkind)
关于枚举的种种第56张关于枚举的种种第57张
关于枚举的种种第1张{
关于枚举的种种第2张Kind=kind;
关于枚举的种种第62张}

关于枚举的种种第2张
关于枚举的种种第2张
privatestringm_Name;
关于枚举的种种第2张
publicstringName
关于枚举的种种第56张关于枚举的种种第57张
关于枚举的种种第1张{
关于枚举的种种第56张关于枚举的种种第57张
get关于枚举的种种第1张{returnm_Name;}
关于枚举的种种第62张}

关于枚举的种种第2张
关于枚举的种种第2张
publicreadonlyEmployeeKindKind;
关于枚举的种种第2张
关于枚举的种种第2张
publicdoubleGetPayment()
关于枚举的种种第56张关于枚举的种种第57张
关于枚举的种种第1张{
关于枚举的种种第2张
switch(Kind)
关于枚举的种种第56张关于枚举的种种第57张
关于枚举的种种第1张{
关于枚举的种种第2张
caseEmployeeKind.Programmer:
关于枚举的种种第2张
//Returnpayment关于枚举的种种第1张
关于枚举的种种第2张
caseEmployeeKind.Salesman:
关于枚举的种种第2张
//Returnpayment关于枚举的种种第1张
关于枚举的种种第2张
caseEmployeeKind.Manager:
关于枚举的种种第2张
//Returnpayment关于枚举的种种第1张
关于枚举的种种第62张
}

关于枚举的种种第62张}

关于枚举的种种第5张}

免责声明:文章转载自《关于枚举的种种》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇图的遍历算法(2)深入理解java:1.2. 字节码执行引擎下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Java枚举的小知识点

enum 是jdk1.5引入的,使用它可以创建枚举类型,就像使用class创建类一样。 enum关键字创建的枚举类型默认是java.lang.Enum(一个抽象类)的子类 用法1 常量一般定义常量都是 public static final …,现在可以把相关常量都放在一个枚举类里,而且枚举比常量提供更多方法 enum season{    Sp...

高效的数据压缩编码方式 Protobuf

一. protocol buffers 是什么? Protocol buffers 是一种语言中立,平台无关,可扩展的序列化数据的格式,可用于通信协议,数据存储等。 Protocol buffers 在序列化数据方面,它是灵活的,高效的。相比于 XML 来说,Protocol buffers 更加小巧,更加快速,更加简单。一旦定义了要处理的数据的数据结构之...

枚举的使用方法

枚举类型在程序开发中是一个非常好用的数据类型,一般用来描述一个审核节点,类型,条件分支是极好的。 我总结枚举类型的使用一般有以下优点: 1. 结构表达清晰 2. 编写条件分支判断不易出错 3. 存储数据空间占用小 1. 定义枚举 byte类型的赋值范围是0~255,对于枚举类型够用了,如果您需要取负值,可以继承int 2. 枚举的使用   3. 获...

QT枚举类型与字符串类型相互转换

Qt5以后(具体版本可能是5.10吧,这个不确定)采用以下方法 https://www.cnblogs.com/dongc/p/5630444.html 在QT中将枚举类型注册(QT_Q_ENUM或QT_Q_FLAG)后,就可以利用QT的元对象进行枚举类型与字符串类型转换了。 代码示例: #include <QtCore/QMetaEnum>...

springboot mybatis自定义枚举enum转换

原文链接:https://blog.csdn.net/u014527058/article/details/62883573 一、概述 在利用Spring进行Web后台开发时,经常会遇到枚举类型的绑定问题。一般情况下,如果Spring接收到的参数值为字符串类型,Spring会根据枚举的值与传入的字符串进行对应。假设有如下枚举 清单1:枚举定义 public...

枚举类型转换成字符串

使用枚举类型默认的ToString()方法,往往不能得到我们想要的输出的字符串。如何方便的定义枚举类型中的每个值代表的字符串输出呢?可以使用DescriptionAttribute, 写上想得到的字符串输出。 enumDirection { [Description("Rover is facing to UP (Negtive Y)")]...