1. 问题
class Tester
{
static void Main()
{
Alignment a = new Alignment();
Console.WriteLine(a.ToString("D"));
Alignment b = Alignment.Left;
Console.WriteLine(b.ToString("D"));
}
}
假定Left是Alignment枚举的第一个成员,你认为这两种初始化枚举变量的方式是否等效?如果不等效,它们有什么差别?
2. 两种初始化方法的对比
2.1 第一个枚举成员的值为0
如果我们没有为Alignment指定第一个成员的值:
enum Alignment
{
Left,
Center,
Right
}
Code #01的输出结果将是:
0
0
我们再把Code #01的Main反编译成IL:
.method private hidebysig static void Main( string [] args) cil managed
{
.entrypoint
// Code Size: 50 byte(s)
.maxstack 2
.locals (
CsWritingLab.Alignment alignment1,
CsWritingLab.Alignment alignment2)
L_0000: nop
L_0001: ldc.i4.0
L_0002: stloc.0
L_0003: ldloc.0
L_0004: box CsWritingLab.Alignment
L_0009: ldstr "D"
L_000e: call instance string [mscorlib]System.Enum::ToString(string)
L_0013: call void [mscorlib]System.Console::WriteLine(string)
L_0018: nop
L_0019: ldc.i4.0
L_001a: stloc.1
L_001b: ldloc.1
L_001c: box CsWritingLab.Alignment
L_0021: ldstr "D"
L_0026: call instance string [mscorlib]System.Enum::ToString(string)
L_002b: call void [mscorlib]System.Console::WriteLine(string)
L_0030: nop
L_0031: ret
}
从上面的代码中,我们可以看到这两种初始化方式是等效的。实质上,下面这4句(在此时)是等效的(它们产生一样的IL代码):
Alignment b = Alignment.Left;
Alignment d = (Alignment)0;
Alignment c = 0;
2.2 第一个枚举成员的值非0
如果我们手动指定Alignment的第一个成员的值呢?
enum Alignment
{
Left = 1,
Center,
Right
}
Code #01的输出结果将有点令人疑惑:
0
1
为什么会这样呢?让我们从IL代码中看看编译器是如何理解此时的Main的:
.method private hidebysig static void Main( string [] args) cil managed
{
.entrypoint
// Code Size: 50 byte(s)
.maxstack 2
.locals (
CsWritingLab.Alignment alignment1,
CsWritingLab.Alignment alignment2)
L_0000: nop
L_0001: ldc.i4.0
L_0002: stloc.0
L_0003: ldloc.0
L_0004: box CsWritingLab.Alignment
L_0009: ldstr "D"
L_000e: call instance string [mscorlib]System.Enum::ToString(string)
L_0013: call void [mscorlib]System.Console::WriteLine(string)
L_0018: nop
L_0019: ldc.i4.1
L_001a: stloc.1
L_001b: ldloc.1
L_001c: box CsWritingLab.Alignment
L_0021: ldstr "D"
L_0026: call instance string [mscorlib]System.Enum::ToString(string)
L_002b: call void [mscorlib]System.Console::WriteLine(string)
L_0030: nop
L_0031: ret
}
从上面的IL代码中,我们可以看出这两种初始化方式已经不再被理解为一样的了。对比Code #03和Code #05,你会发现,改变的仅仅是L_0019行:
ldc.i4.0 -> ldc.i4.1
也就是说,使用枚举的第一个成员来初始化枚举变量,编译器懂得根据枚举的定义来作出相应的调整,从而编译出符合我们预期的代码。这种处理方式实际上使用了多态性的思维。
而此时,
相当于
或者
3. new、值类型的默认构造函数和值类型的默认值
通常我们使用new来调用引用类型的实例构造函数(Instance Constructors),或者自定义值类型的非默认实例构造函数(Non-Default Instance Constructors)。然而,我们也可以使用new来调用值类型(包括内置简单类型和自定义类型)的默认构造函数,例如:
这里,new调用int的默认构造函数把i初始化为对应的默认值——0。当然,这个默认构造函数由.NET自动提供(但你不能手动提供)。也就是说,使用new来调用值类型的默认构造函数,该值类型将被自动设为对应的默认值。.NET的值类型分为简单类型(Simple types)、枚举类型(Enum types)和结构类型(Struct types)。
3.1 简单类型(Simple types)的默认值
对于简单类型(Simple types),它们的默认值如下表所示:
Simple Type
|
Default Value
|
bool | false |
byte | 0 |
char | '\0' |
decimal | 0.0M |
double | 0.0D |
float | 0.0F |
int | 0 |
long | 0L |
sbyte | 0 |
short | 0 |
uint | 0 |
ulong | 0 |
ushort | 0 |
3.2 枚举类型(Enum types)的默认值
对于枚举类型(Enum types),.NET会自动将字面值0(literal 0)隐式地转换为对应的枚举类型。
3.2.1 有一个0值成员
如果枚举类型中的某个成员被赋予0值(不要求是第一个成员),那么枚举变量所储存的值就是该成员的值。假定Alignment的成员被赋值如下:
enum Alignment
{
Left = 1,
Center = 0,
Right = 2
}
那么,下面这句
将等效于
3.2.2 没有0值成员
如果枚举类型中任何一个成员都不为0,例如
enum Alignment
{
Left = 1,
Center = 2,
Right = 3
}
那么
将等效于
或者
而此时,枚举变量a所储存的值我们可以称为非预定义枚举(成员)值。
3.2.3 有两个或以上的0值成员
那么,如果枚举类型里存在多于一个成员被赋予0值呢?例如
enum Alignment
{
Left = 0,
Center = 1,
Right = 0
}
你能猜得出下面代码的运行结果吗?
Alignment a = new Alignment();
Console.WriteLine(a.ToString());
从该代码的运行结果中我们可以看到,new把Alignment.Left“许配”给枚举变量a。现在让我们看看下面这段代码:
string a = Enum.GetName( typeof (Alignment), 0);
Console.WriteLine(a.ToString());
其实,Code #10和Code #09的输出结果一样的,从.NET的源代码中我们也可以看到,选择对象的规则是先用Array.Sort(Array keys, Array items);对枚举成员名称及其值进行排序,再用循环挑选第一个出现的幸运儿。
3.3 结构类型(Struct types)的默认值
对于结构类型(Struct types),其所包含的值类型字段会被初始化为对应的默认值,而引用类型字段会被初始化为null。
// See Code #02 for Alignment.
public struct MyStruct
{
public int Integer;
public Alignment Align;
public string Text;
}
那么,如下代码:
Console.WriteLine(m.Integer);
Console.WriteLine(m.Align);
Console.WriteLine(m.Text == null );
的运行结果将是:
0
Left
True
4. 你认为如何使用枚举才恰当?
现在,把本文之前的都忘了,认真地考虑一下这个问题:
你认为如何使用枚举才恰当?