枚举
本书迄今介绍的每种类型(除string
外)都有明确的取值范围。诚然,有些类型(如double
)的取值范围非常大,可以看成是连续的,但它们仍是一个固定集合。最简单的示例是bool
类型,它只能取两个值:true
或false
。 有时希望变量取的是一个固定集合中的值。例如,让orientation
类型可以存储north
、south
、east
或west
中的一个值。 此时可以使用枚举类型。枚举可以完成这个orientation
类型的任务:它们允许定义一个类型,其取值范围是用户提供的值的有限集合。所以,需要创建自己的枚举类型orientation
,它可以从上述 4 个值中提取一个值。 注意有一个附加步骤—不是仅仅声明一个给定类型的变量,而是声明和描述一个用户定义的类型,再声明这个新类型的变量。
定义枚举
可以用 enum
关键字来定义枚举,如下所示:
enum <typeName>
{
<value1>,
<value2>,
<value3>,
...
<valueN>
}
接着声明这个新类型的变量:
<typeName> <varName>;
并赋值:
<varName> = <typeName>.<value>;
枚举使用一个基本类型来存储。枚举类型可取的每个值都存储为该基本类型的一个值,默认情况下该类型为 int
。在枚举声明中添加类型,就可以指定其他基本类型:
enum <typeName> : <underlyingType>
{
<value1>,
<value2>,
<value3>,
...
<valueN>
}
枚举的基本类型可以是 byte
、sbyte
、short
、ushort
、int
、uint
、long
和 ulong
。
默认情况下,每个值都会根据定义的顺序(从 0 开始),被自动赋予对应的基本类型值。这意味着 <value1>
的值是 0,<value2>
的值是 1,<value3>
的值是 2 等。可以重写这个赋值过程:使用 =
运算符,指定每个枚举的实际值:
enum <typeName> : <underlyingType>
{
<value1> = <actualVal1>,
<value2> = <actualVal2>,
<value3> = <actualVal3>,
...
<valueN> = <actualValN>
}
还可以使用一个值作为另一个枚举的基础值,为多个枚举指定相同的值:
enum <typeName> : <underlyingType>
{
<value1> = <actualVal1>,
<value2> = <value1>,
<value3>,
...
<valueN> = <actualValN>
}
未赋值的任何值都会自动获得一个初始值,这里使用的值是从比上一个明确声明的值大于 1 开始的序列。例如,在上面的代码中,<value3>
的值是 <value1> + 1
。
注意 ⚠️这可能会产生预料不到的问题,在一个定义(如 <value2> = <value1>
)后指定的值可能与其他值相同。例如,在下面的代码中,<value4>
的值与 <value2>
相同。
enum <typeName> : <underlyingType>
{
<value1> = <actualVal1>,
<value2>,
<value3> = <value1>,
<vaue4>,
...
<valueN> = <actualValN>
}
当然,如果这正是希望的结果,则代码就是正确的。还要注意,以循环方式赋值可能会产生错误,例如:
enum <typeName> : <underlyingType>
{
<value1> = <value2>,
<value2> = <value1>,
}
下面看一个示例。其代码定义了一个枚举orientation
,然后演示了它的用法。把下列代码添加到Program.cs
中:
namespace Ch05Ex02
{
enum orientation : byte
{
north = 1,
south = 2,
east = 3,
west = 4
}
class Program
{
static void Main(string[] args)
{
byte directionByte;
string directionString;
orientation myDirection = orientation.north;
Console.WriteLine("myDirection = {0}", myDirection);
directionString = Convert.ToString(myDirection);
Console.WriteLine("byte equivalent = {0}", directionByte);
Console.WriteLine("string equivalent = {0}", directionString);
Console.ReadKey();
}
}
}
示例的说明这段代码定义并使用了一个枚举类型orientation
。首先要注意的是,类型定义代码放在名称空间Ch05EX02
中,但没有与其余代码放在一起。这是因为在运行期间,定义代码并不是像执行应用程序中的代码那样一行一行地执行。应用程序是从我们熟悉的位置开始执行的,它可以访问新类型,因为该类型位于同一个名称空间中。 这个示例的第一个迭代演示了创建新类型的变量,给它赋值以及把它输出到屏幕上的基本方法。接着修改代码,把枚举值转换为其他类型。注意这里必须使用显式转换。即使orientation
的基本类型是byte
,仍必须使用(byte
)强制实现类型转换,把myDirection
的值转换为byte
类型:
directionByte = (byte)myDirection;
如果要将byte
类型转换为orientation
,也同样需要进行显式转换。例如,可以使用下述代码将byte
变量myByte
转换为orientation
,并将这个值赋给 myDirection:
myDirection = (orientation)myByte;
当然,这里必须小心,因为并不是所有byte
类型变量的值都可以映射为已定义的orientation
值。orientation
类型可以存储其他byte
值,所以这么做不会直接产生一个错误,但会在应用程序的后面违反逻辑。
要获得枚举的字符串值,可以使用 Convert.ToString():
directionString = Convert.ToString(myDirection);
使用( string
)强制类型转换是行不通的,因为需要进行的处理并不仅是把存储在枚举变量中的数据放在 string
变量中,而是更复杂一些。另外,可以使用变量本身的 ToString()
命令。下面的代码与使用 Convert.ToString()
的效果相同:
directionString = myDrection.ToString();
也可以把 string
转换为枚举值,但其语法稍微复杂一些。有一个特定的命令用于完成此类转换,即 Enum.Parse()
,其用法如下:
(enumerationType)Enum.Parse(typeof(enumerationType), enumerationValueString);
这里使用了另一个运算符 typeof
,它可以得到操作数的类型。对 orientation
类型使用这个命令,如下所示:
string myString = "north";
orientation myDirection = (orientation)Enum.Parse(typeof(orientation), myString);
当然,并非所有字符串值都会映射为一个 orientation
值。如果传送的一个值不能映射为枚举值中的一个,就会产生错误。与C#中的其他值一样,这些值是区分大小写的,所以如果字符串与一个值相同,但大小写不同(例如,myString
设置为 North
,而不是 north
),就会产生错误。