关于枚举的种种
BUG吗?虫虫吗?
using
System;
using
System.Collections.Generic;
using
System.Text;
using
System.Web;
namespace
枚举值装箱为基类所实现之接口
{
enum
AlignMent{Left
=
100
,Right
=
1000
,Center
=
10
};
class
Program
{
static
void
Main(
string
[] args)
{
//
著名,此试验在.NET 1.1下无法通过,1.x不支持枚举隐式装箱
//
试验目的:枚举值装箱成其基类所实现之接口(隐式装箱)
//
第一种:能够允许,说明了枚举值(值类型)能够转换为其基类实现了的接口对象,并且是显式转换
IConvertible ic
=
(IConvertible)AlignMent.Center;
int
i
=
ic.ToInt32(
null
);
Console.WriteLine(
"
第一次转换的枚举值Center结果为{0}
"
, i);
//
程序暂停
Console.ReadKey();
Console.WriteLine();
IConvertible ic2
=
(System.Enum)AlignMent.Left;
i
=
ic2.ToInt32(
null
);
Console.WriteLine(
"
第二次转换(使用的是Enum对象),结果是{0}
"
, i);
Console.ReadKey();
Console.WriteLine();
ic2
=
AlignMent.Right;
i
=
ic2.ToInt32(
null
);
Console.WriteLine(
"
第三次转换,是隐式转换为接口对象,结果是{0}
"
, i);
Console.ReadKey();
}
}
}
枚举类型都是值类型。
System.Enum不是枚举类型,它属于引用类型.System.Enum是一个抽象类(abstract class),所有枚举类型都直接继承自它,当然也同时继承了它的所有成员。
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),但本身却是一个引用类型。
//
目的:看System.enum是不是枚举类型;看它是不是值类型
Type t
=
typeof
(System.Enum);
if
(t.IsEnum)
{
Console.WriteLine(
"
System.Enum是enum type.
"
);
}
else
{
Console.WriteLine(
"
System.Enum<不>是enum type.
"
);
//
执行这里
}
if
(t.IsValueType)
{
Console.WriteLine(
"
System.Enum是value type.
"
);
}
else
{
Console.WriteLine(
"
System.Enum<不>是enum type.
"
);
//
执行这里
}
//
很奇怪,原因全在编译器,System.Enum很特殊.
枚举类型可以被装箱成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。对应的装箱操作既可以为隐式的也可以是显式的。
Q:我们如何获取枚举成员的值,无论成员是否被手动赋值?
A:你可以使用System.Enum的
public
static
Array GetValues(Type enumType);
该方法返回一个包含所有枚举成员的数组:
//
目的:使用System.Enum的GetValues()方法,它会返回所有枚举成员的数组
AlignMent[] alignments
=
(AlignMent[])Enum.GetValues(
typeof
(AlignMent));
Console.WriteLine(
"
下面列出Alignment枚举类型所有成员:
"
);
foreach
(AlignMent a
in
alignments)
{
Console.WriteLine(
"
\t{0:G}={0:D}
"
, a);
}
用System.Enum的
public
static
bool
IsDefined(Type enumType,
object
value);
来判断是否一个整数值能够转换为枚举成员值.
//
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
}
这样,恶作剧的人将会收到一个警告(异常消息)
还可以使用条件判断语句来处理这种情况:
//
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:这个时候你就需要位枚举(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
);