C#枚举类型的默认值一定是0说起
来源:互联网 发布:mac迅雷一直是0 编辑:程序博客网 时间:2024/06/05 01:03
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),它们的默认值如下表所示:
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. 你认为如何使用枚举才恰当?
现在,把本文之前的都忘了,认真地考虑一下这个问题:
你认为如何使用枚举才恰当?
- C#枚举类型的默认值一定是0说起
- 从枚举类型的ordinal()方法说起
- C# 值类型的默认值
- C#中的特定类型数组的默认值
- C# DateTime类型的变量默认值
- C# 枚举类型的扩展
- C# 枚举类型的转换
- C#控制台基础 被线程执行的函数如果有参数的话,那么参数的类型一定是object
- C#中值类型的枚举类型
- 比较运算符的结果一定是boolean类型
- 运算符两边操作数一定是同类型的
- 对象的基本类型为什么默认值是0对象引用被设为Null
- 枚举一定条件的子集
- 返回类型是枚举
- 刨根问底:C++中宽字符类型(wchar_t)的编码一定是Unicode?长度一定是16位?
- C#枚举类型的使用示例
- C#中枚举类型的转换问题
- C#枚举类型的使用示例
- HGDB重建模板数据库的方法(适用于PG)
- 7 局部求和
- 美女头像这么多,他们用大数据告诉你哪个是骗子
- 搭建java环境\Tomcat安装和基本配置\Tomcat_MSM
- Windows中Django安装教程
- C#枚举类型的默认值一定是0说起
- KMP字符匹配
- TCP/IP的开肠破肚(1)
- test
- 谈谈服务治理
- 关于深度优先搜索个人总结
- 如果你没有钱,没有能力,没有人脉,你就这样干!
- 底半部机制
- HTML5表单自定义验证setCustomValidity